PHP SEO: the six things a PHP site controls in its own code, from status codes and redirects to clean URLs
What PHP changes about how Google crawls and indexes a site, written from the PHP manual and Google Search Central as both read on 22 October 2026.
Published by AI Knows Us · Updated 21 October 2026
PHP is neither good nor bad for SEO. Google never sees your PHP code. It sees the HTTP response your server sends: a status code, some headers and an HTML page. A PHP site is server-rendered by default, which Google's JavaScript documentation describes as the case that "works well", because the content is in the HTML from the start. What PHP does control is the detail of that response, and that is where PHP sites win or lose in search: the status code on a missing page, the type of redirect, the shape of the URL, the title and canonical tags in the template, session IDs, and the sitemap. This page covers each one with the PHP function that sets it.
1. Send the right status code for a missing page
The most common PHP SEO fault is a "not found" page that returns 200. A script like product.php?id=999 looks up a record, finds nothing, prints "Sorry, no such product" and exits with the default 200 status. Google's documentation says that when a 2xx page's content suggests an error, Search Console reports it as a soft 404.
The fix is one line. The PHP manual describes http_response_code() as the function that "Gets or sets the HTTP response status code":
<?php
$product = find_product($_GET['id'] ?? '');
if (!$product) { http_response_code(404); include 'templates/not-found.php'; exit;
}
Google says it does not index URLs that return a 4xx code, and that already indexed URLs returning 4xx are removed. Use 404 or 410 for pages that are really gone. Do not use 401 or 403 to slow crawling; Google says they have no effect on crawl rate.
Server errors are different. A 500 or 503 makes Google's crawlers slow down, and Google says URLs that "persistently return a server error" are removed from the index. If a database outage makes every page throw a 500, fix the outage first; the status code is telling the truth.
2. Make permanent redirects permanent
This one catches experienced developers. The PHP manual says that sending a Location: header also returns a "REDIRECT (302) status code to the browser unless the 201 or a 3xx status code has already been set." So a bare header('Location: ...') is a temporary redirect.
Google treats the two differently. Its HTTP status code documentation says a 301 is "a strong signal that the redirect target should be processed", while a 302 is "a weak signal". When a page has moved for good, send the 301 explicitly. Google's own redirect documentation gives this PHP example:
<?php
header('HTTP/1.1 301 Moved Permanently');
header('Location: https://www.example.com/newurl');
exit();
The same result in one call uses the third parameter of header(), which the manual says "Forces the HTTP response code to the specified value":
<?php
header('Location: https://www.example.com/newurl', true, 301);
exit;
Two rules from the PHP manual keep redirects working. First, header() "must be called before any actual output is sent", including blank lines or spaces in an included file. That is the cause of the familiar "headers already sent" warning. Second, call exit after the redirect so the rest of the script does not run. For the wider picture of moving pages, our guide to 301 redirects and what Google does with them covers chains and mapping old URLs to new ones.
3. Turn script URLs into descriptive URLs
Google's URL structure guide, updated 21 October 2026, uses a PHP address as its "not recommended" example: index.php?topic=42&area=3a5ebc944f41daa6f849f730f1, set against a readable one like /wiki/Aviation. Its recommendations:
- Use readable words rather than long ID numbers.
- Use hyphens, not underscores, to separate words.
- If you use parameters, use the common form:
=between key and value and&between pairs. - Do not use URL fragments (the part after
#) to change content.
In PHP the usual pattern is a front controller: the web server sends every request to one index.php, which reads the path and loads the right page. On Apache this is usually a mod_rewrite rule, the same module Google's redirect documentation points to for complex redirects. Once the new URLs are live, 301 each old ?id= address to its new form, one to one.
4. Build the title, description and canonical in the template
A PHP template prints the same <head> on every page unless you tell it not to, which is how sites end up with every page titled "Home". Pass each page's own title, description and canonical URL into the layout, and escape them. The PHP manual's htmlspecialchars() converts special characters to HTML entities, and since PHP 8.1.0 its default flags are ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, so quotes in a product name cannot break the attribute.
<?php $e = fn($s) => htmlspecialchars($s); ?>
<title><?= $e($page['title']) ?></title>
<meta name="description" content="<?= $e($page['summary']) ?>">
<link rel="canonical" href="https://www.example.com/<?= $e($page['path']) ?>">
Print the canonical as a full URL on your preferred host. Google lists the canonical signals in order of strength: redirects and rel="canonical" are strong signals, and inclusion in a sitemap is a weak one. They stack, so a PHP site that redirects stray versions, prints a canonical tag and lists only canonical URLs in its sitemap is sending all three at once.
5. Keep session IDs out of URLs
Google's URL guide lists session IDs in URLs as a source of endless duplicate addresses and says "Wherever possible, avoid the use of session IDs in URLs and consider using cookies instead." PHP already does this by default. The PHP manual lists session.use_only_cookies as defaulting to 1 and session.use_trans_sid as defaulting to 0, and since PHP 8.4.0, turning on transparent session IDs is deprecated. If you inherit an old site with PHPSESSID= in its links, check the php.ini for those two settings first.
6. Generate the sitemap from the database
A PHP script can write sitemap.xml from the same table that drives the pages, so it is never out of date. Google's sitemap documentation sets the limits: 50,000 URLs or 50MB uncompressed per file, UTF-8 encoding, and fully qualified absolute URLs. Google says it "ignores <priority> and <changefreq> values" and uses <lastmod> when it is consistently accurate, so print the real date each record last changed and leave the other two out.
| Symptom in Search Console | Likely PHP cause | Fix |
|---|---|---|
| Soft 404 | Empty record page returns 200 | http_response_code(404) |
| Old URL still indexed after a move | Bare Location: header sends 302 | Send 301 explicitly |
| Duplicate pages with different URLs | Parameters, session IDs or no canonical | Canonical tag, cookies for sessions, redirects |
| Many pages with the same title | Hard-coded <title> in the layout | Pass page data into the template |
What an assistant reads from a PHP page
The same response that Googlebot reads is what an AI assistant's crawler reads when it fetches your page to answer a question. A server-rendered PHP page has an advantage here, because the answer is in the HTML on the first request with nothing to run. The weak points are the same ones listed above: a soft 404 that looks like a real page, or a template that gives every page the same title, makes it harder for an assistant to tell what each page is about. Once the technical layer is clean, the useful question is whether ChatGPT, Gemini or Perplexity actually name your business when a buyer asks. AI Knows Us checks that and shows which pages and other sites each answer is built from.
Questions about PHP and SEO
Is PHP bad for SEO?
No. PHP renders HTML on the server, and Google's documentation says server-rendered pages, where the HTML contains all the content, work well for crawling. Problems come from how a site uses PHP, not from PHP itself.
Should I use header() or http_response_code() to send a 404?
Either works. The PHP manual lists http_response_code(404) as a better alternative, in typical use, to writing the HTTP/1.1 404 Not Found line by hand.
Do .php endings in URLs hurt rankings?
Google's URL structure guide asks for readable words, hyphens and simple parameters. A .php ending is not on its list of problems. If you do remove extensions, redirect each old URL to its new one with a 301.
Why does my redirect show as 302?
Because a Location: header sends 302 by default, according to the PHP manual. Pass 301 as the third argument to header(), or send the 301 status line first.
How do I fix "headers already sent"?
Something was output before header() ran, often a blank line or space before <?php or after ?> in an included file. Remove it, or move the redirect logic above any output.