# Generate a QR code in PHP

> Install endroid/qr-code with Composer, construct a QrCode, and write it with PngWriter: saveToFile, getString or getDataUri cover files, HTTP responses and inline embedding. With no Composer available, file_get_contents against the keyless API and file_put_contents the bytes. Verify styled codes with GET /api/v1/verify.

Source: https://useqr.app/docs/developers/generate-a-qr-code-in-php · Last reviewed 2026-08-21 · UseQR is free forever, no signup.

---

## endroid/qr-code

The maintained standard in the PHP ecosystem:

```bash
composer require endroid/qr-code
```

```php
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;

$qrCode = new QrCode('https://example.com');
$writer = new PngWriter();
$result = $writer->write($qrCode);

$result->saveToFile(__DIR__ . '/qr.png');
```

The result object is flexible about where the bytes go:

```php
// serve directly from a controller or plain script
header('Content-Type: ' . $result->getMimeType());
echo $result->getString();

// or embed inline in HTML
echo '<img src="' . $result->getDataUri() . '" width="256" alt="QR code">';
```

`PngWriter` renders through the GD extension, which ships enabled in most PHP builds, if
`saveToFile` throws about image functions, GD is the thing to check. The package also has
an `SvgWriter`, which is what you want for anything headed to
[print](/docs/print/qr-code-size-for-print). Whatever writer you use, leave the default
margin alone: the 4-module [quiet zone](/glossary/quiet-zone) is the first casualty of
"tidying up" and the most common reason a code
[stops scanning](/docs/troubleshooting/qr-code-not-scanning-checklist).

## No Composer? Two lines

Shared hosting with no dependency manager is still common in PHP land. The
[keyless API](/docs/developers/free-qr-code-api-no-key) needs nothing but
`allow_url_fopen`:

```php
$data = urlencode('https://example.com/sale?src=poster');
$png = file_get_contents("https://useqr.app/api/v1/qr?data={$data}&size=1024");
file_put_contents(__DIR__ . '/qr.png', $png);
```

`urlencode` is mandatory, a raw `&` in the payload becomes a second query parameter and
the encoded data is truncated with no warning. Add `format=svg` for vector output, or
`ec=H` when a logo will sit on the code. Typed endpoints (`/api/v1/wifi`,
`/api/v1/vcard`, `/api/v1/upi`) build and validate the payload server-side, which is worth
having: WiFi string escaping and UPI field rules are exactly where hand-rolled PHP goes
wrong.

One caveat: do not send credentials (WiFi passwords, personal data) through any remote
API, generate those locally with endroid/qr-code instead.

## WordPress

In a theme or plugin, either bundle endroid/qr-code via Composer or output an `<img>`
pointing at the API: the response is cacheable for a year, so it adds no meaningful load
time. For the no-code routes (blocks, plugins, widgets), see
[how to add a QR code to WordPress](/docs/how-to/how-to-add-a-qr-code-to-wordpress).

## Verify before shipping

If you style a code (colours, logo, custom modules) decode it back before it goes out:

```php
$report = json_decode(file_get_contents(
    "https://useqr.app/api/v1/verify?data={$data}&color=6366f1"
), true);
if (!$report['scannable']) {
    throw new RuntimeException(implode('; ', $report['issues']));
}
```

The endpoint renders, rasterises and reads the code with a real decoder, the
[decode-verify loop](/docs/developers/why-verify-that-your-qr-code-decodes) in one GET.

## FAQ

### What is the best PHP library for QR codes?
endroid/qr-code. It is actively maintained, installs via Composer, and writes PNG and SVG to files, strings or data URIs.

### Can I make a QR code in PHP without Composer?
Yes, file_get_contents against the keyless API and file_put_contents the returned bytes. It works on shared hosting with allow_url_fopen enabled.

### How do I serve a QR code directly from a PHP script?
Send the writer result's MIME type as the Content-Type header and echo getString(), or point an img tag at a data URI from getDataUri().

### Why does endroid/qr-code fail with an image error?
Almost always the GD extension. PngWriter renders through GD; enable it in php.ini or switch to SvgWriter, which does not need it.

## Try it

- https://useqr.app/url
- https://useqr.app/json
- https://useqr.app/validate
