Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Random User Agent Laravel Package

campo/random-user-agent

Campo Random User Agent is a Laravel/PHP package that generates realistic random user-agent strings on demand. Rotate browser identities in scrapers, tests, or request simulations using simple helpers and configurable lists of desktop and mobile agents.

View on GitHub
Deep Wiki
Context7

Getting Started

Install via Composer — there is no config to publish and (on Laravel 5.5+) no provider to register manually:

composer require campo/random-user-agent

The package is a thin wrapper around a bundled list of user-agent strings. The primary entry point is a static call:

use Campo\RandomUserAgent\RandomUserAgent;

$ua = RandomUserAgent::generate();
// "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 ..."

Look here first: vendor/campo/random-user-agent/src/ for the class, and the data file it reads (a JSON/text list of UA strings). That file — not the PHP — is the real substance of the package. Skim it before you build anything on top; you need to know how stale the strings are and whether they're grouped by device/browser. As of 1.3.0 the class parses that file once per process and reuses the result, so the file is read on the first generate() call in a process and never again.

First use case: filling a user_agent column in a seeder, or attaching a realistic User-Agent header to an outgoing request so you don't get fingerprinted as a default Guzzle/curl client:

Http::withHeaders(['User-Agent' => RandomUserAgent::generate()])->get($url);

That single line is 90% of what this package is good for. Everything else is plumbing around it.

Implementation Patterns

Wrap it, don't call it directly. A static method returning a random string is untestable and unsubstitutable. Bind an interface once and inject that everywhere:

// app/Support/UserAgentGenerator.php
interface UserAgentGenerator { public function get(): string; }

// AppServiceProvider::register()
$this->app->bind(UserAgentGenerator::class, fn () => new class implements UserAgentGenerator {
    public function get(): string { return RandomUserAgent::generate(); }
});

Now a test can swap in a fixed generator, and swapping the underlying package (see Gotchas) is a one-line change.

Seeder / factory usage. Faker has no real-UA provider worth using; this gives plausible browser strings:

public function definition(): array
{
    return [
        'user_agent' => RandomUserAgent::generate(),
        'ip_address' => fake()->ipv4(),
    ];
}

Per-job rotation for scraping queues. Generate a UA per job, not per application boot, so a long-running worker doesn't hammer one string across hundreds of requests:

class FetchPage implements ShouldQueue
{
    public function handle(UserAgentGenerator $uas): void
    {
        Http::withHeaders(['User-Agent' => $uas->get()])
            ->retry(3, 500)
            ->get($this->url);
    }
}

Distinct-set generation. If you need N different agents (e.g. a pool for concurrent workers), the package doesn't dedupe for you — collect and filter:

$pool = collect(range(1, 50))
    ->map(fn () => $uas->get())
    ->unique()
    ->values();

As of 1.3.0 the underlying list is parsed once per process, so drawing thousands of values in a loop or seeding a large table is now cheap — no need to pre-load the file into a cached array yourself. If you want weighted or filtered draws you still need your own copy of the list (see Gotchas).

Test fixtures. Pair it with Http::fake() when you want the fake to assert on a realistic header:

Http::fake();
Http::withHeaders(['User-Agent' => $uas->get()])->get('https://example.com');
Http::assertSent(fn ($req) => str_starts_with($req->header('User-Agent')[0], 'Mozilla/'));

Gotchas and Tips

It's frozen at 2018 (data-wise). No meaningful data refresh in years, and 1.3.0 is a performance patch, not a content update — the strings are the same ones that shipped with the 1.x line. Treat it as vendored code: pin the exact version, and be ready to inline the data file yourself if a PHP 8.x deprecation breaks the class. The migration cost is genuinely low — copy the JSON list into resources/data/user-agents.json and replace the call with Arr::random(json_decode(file_get_contents(...), true)).

The strings are stale. The bundled list predates modern Chrome/Safari version numbers. For anything where a server checks UA plausibility (Cloudflare, Akamai, most anti-bot stacks), an eight-year-old UA is more suspicious than the default Guzzle string. Verify against a current UA list before relying on this for scraping.

The list is cached for the life of the process (1.3.0+). This is the headline change: generate() no longer re-reads and re-parses the data file on every call. Consequences worth knowing:

  • Fast loops, seeders, and queue workers are now free — drawing 10,000 agents in one process is fine.
  • The cache lives in static state, so any failure on the first call is also cached. If the data-file read fails on that first invocation (see next tip), every subsequent call in that worker returns the same empty/failed result rather than recovering. Don't assume a retry will fix it — restart the worker.
  • Editing or hot-swapping the data file at runtime has no effect until the process restarts. Don't build a "refresh the UA list" feature on top of this package; that's a fork-the-data situation.
  • Under php artisan serve/FPM the "process" is per-request-ish, so the benefit is mostly in workers, seeders, and long-running commands — which is exactly where it mattered.

Non-deterministic — never assert on the value. You cannot seed it. Assertions must be structural (strlen > 20, starts_with('Mozilla/'), header present), never equality. If you need reproducibility, inject a fake generator via the binding above.

Duplicates are expected. Random draws repeat. Any logic that assumes "N calls = N unique agents" is a bug — always unique() explicitly. This is unchanged by 1.3.0; caching the list does not make the draws distinct.

No filtering API to rely on. Whether the class exposes device/browser/OS-scoped generation depends on the version you land on; check the README before designing around it. If it doesn't, filter the underlying list yourself rather than fighting the static API. With the list now cached internally and not exposed, "filter it yourself" increasingly means keeping your own copy.

Empty string means a path problem. The class resolves its data file relative to __DIR__. If you relocate vendor/ (rare, but happens with some deploy pipelines and PHAR builds), the read fails silently and you get '' — and post-1.3.0 that failure is cached, so it won't self-heal within the process. Debug with a raw dd(RandomUserAgent::generate()) on a fresh process before assuming anything about headers.

Don't use it for anything security-adjacent. It's cosmetic string generation — no validation, no guarantees, no CSPRNG involvement. It belongs in seeders, test fixtures, and outbound header decoration, and nowhere else.

Extension point is the data file. There's no meaningful hook or config layer — 1.3.0 added an internal memoization optimization, not a public API. If you need freshness, filtering, weighting, or determinism, the honest move is still to extract the JSON into your own small class — this is the rare case where "fork the data, drop the dependency" is the right call rather than wrapping harder.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ably/pubsub-server
mstfkhazaal/defender
mfonte/base62x
karnoweb/metapilot
tobya/bcsapiwrapper
swalker2/cpanel
vish4395/laravel-translation-manager
morningtrain/economic
teksite/systeminfo-laravel
atomcms/voting
run6/weather
siokas/laravelembeddirectives
adamlien/pkuuids
gilsonsouza/lara-doc-generator
manelgavalda/enrollment_mobile
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics