DDEV SEO URL Redirect
August 7, 2025 ยท AI Conversations
but when open any url its redircto home page
—
### โ 1. **Check DDEV's `nginx/apache` Document Root Configuration**
DDEV needs to know that your siteโs **document root is `src/`**. You must configure this in `.ddev/config.yaml`.
#### ๐ง Fix:
Open `.ddev/config.yaml` and update the `docroot`:
docroot: src
Then restart DDEV:
ddev restart
—
### โ 2. **Check `.htaccess` Rewrite Rules**
If you're using SEO URLs like `/about` or `/products/item`, your `.htaccess` should have proper rewrite rules to forward all non-file requests to `index.php`.
Hereโs a working example of a minimal `.htaccess` inside the `src/` folder:
RewriteEngine On
# Redirect requests that are not files or directories to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
Make sure this file is **inside the `src/` folder**, as thatโs your document root.
—
### โ 3. **Check `index.php` Router Logic**
In your `src/index.php`, you likely have custom routing logic. If all pages go to the homepage, your routing logic might be defaulting everything to the home view.
#### ๐ง Fix:
Make sure `index.php` reads and interprets the `$_SERVER['REQUEST_URI']` correctly, e.g.:
$request = trim($_SERVER['REQUEST_URI'], '/');
switch ($request) {
case '':
case 'home':
require 'home.php';
break;
case 'about':
require 'about.php';
break;
// more routes here
default:
http_response_code(404);
require '404.php';
break;
}
—
### โ 4. **Verify Apache is Used in DDEV**
Ensure your `.ddev/config.yaml` is using **Apache**, not Nginx, since `.htaccess` works only with Apache.
webserver_type: apache-fpm
Then run:
ddev restart
—
### โ 5. **Flush DDEV Router and Clear Browser Cache**
Run:
ddev restart
ddev poweroff
ddev start
Also try a private/incognito browser window to avoid cached redirects.
—
If the issue persists after trying all the above, feel free to share your `.htaccess` and `index.php` file snippets so I can help debug further.
1. ๐ซ `.htaccess` rewrite rules not being executed properly
2. โ ๏ธ `index.php` route handling is incorrect
—
### โ Letโs verify step-by-step:
#### ๐ Step 1: Check `.htaccess` inside `src/`
Ensure your `.htaccess` (in `src/.htaccess`) has this:
RewriteEngine On
# Skip existing files/directories
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Route everything else to index.php
RewriteRule ^(.*)$ index.php [QSA,L]
—
#### ๐ Step 2: Check `index.php` routing logic
Inside `src/index.php`, try a simple router like this to test:
<?php
$request = trim(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH), '/');
switch ($request) {
case '':
case 'home':
echo "This is the Home Page";
break;
case 'about':
echo "This is the About Page";
break;
default:
echo "404 Page not found: $request";
http_response_code(404);
break;
}
Now visit:
– `http://project.test/` โ should show โThis is the Home Pageโ
– `http://project.test/about` โ should show โThis is the About Pageโ
– Anything else should show a custom 404
—
#### ๐ Step 3: Confirm Apache is used in DDEV
Run this command inside your DDEV project:
ddev describe
Look for:
webserver_type: apache-fpm
If it's using `nginx-fpm`, update `.ddev/config.yaml`:
webserver_type: apache-fpm
Then run:
ddev restart
—
#### ๐ Step 4: Enable `.htaccess` debugging (Optional)
To confirm `.htaccess` is being executed, add an invalid directive inside it (like `XYZTestDirective`) โ if it breaks with a 500 error, then `.htaccess` is being read. If not, it's being ignored.
—
### Final Thoughts
If after all this, **every custom URL still goes to the homepage**, it's almost certainly because your `index.php` is defaulting to home for all paths (e.g. `require 'home.php';` at the top). The routing logic needs to correctly parse the URL and route to the appropriate content.
If you're stuck, share your `.htaccess` and `index.php` content and Iโll inspect it directly.
# # FOR PRODUCTION
# AddType application/vnd.ms-fontobject .eot
# AddType application/x-font-ttf .ttf
# AddType application/x-font-opentype .otf
# AddType application/x-font-woff .woff
# AddType application/x-font-woff2 .woff2
# AddType image/svg+xml .svg
# # compress text, HTML, JavaScript, CSS, and XML
# AddOutputFilterByType DEFLATE application/javascript
# AddOutputFilterByType DEFLATE application/rss+xml
# AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
# AddOutputFilterByType DEFLATE application/x-font
# AddOutputFilterByType DEFLATE application/x-font-opentype
# AddOutputFilterByType DEFLATE application/x-font-otf
# AddOutputFilterByType DEFLATE application/x-font-truetype
# AddOutputFilterByType DEFLATE application/x-font-ttf
# AddOutputFilterByType DEFLATE application/x-javascript
# AddOutputFilterByType DEFLATE application/xhtml+xml
# AddOutputFilterByType DEFLATE application/xml
# AddOutputFilterByType DEFLATE font/opentype
# AddOutputFilterByType DEFLATE font/otf
# AddOutputFilterByType DEFLATE font/ttf
# AddOutputFilterByType DEFLATE image/svg+xml
# AddOutputFilterByType DEFLATE image/x-icon
# AddOutputFilterByType DEFLATE text/css
# AddOutputFilterByType DEFLATE text/html
# AddOutputFilterByType DEFLATE text/javascript
# AddOutputFilterByType DEFLATE text/plain
# AddOutputFilterByType DEFLATE text/xml
# # remove browser bugs
# BrowserMatch ^Mozilla/4 gzip-only-text/html
# BrowserMatch ^Mozilla/4.0[678] no-gzip
# BrowserMatch bMSIE !no-gzip !gzip-only-text/html
# Header append Vary User-Agent
# ExpiresActive On
# ExpiresDefault "access plus 7 days"
# ExpiresByType image/gif "access plus 1 year"
# ExpiresByType image/png "access plus 1 year"
# ExpiresByType image/jpg "access plus 1 year"
# ExpiresByType image/jpeg "access plus 1 year"
# ExpiresByType image/svg "access plus 1 year"
# ExpiresByType image/webp "access plus 1 year"
# ExpiresByType image/svg+xml "access plus 1 year"
# ExpiresByType image/x-icon "access plus 1 year"
# ExpiresByType text/html "access plus 2 weeks"
# ExpiresByType text/css "access plus 20 weeks"
# ExpiresByType text/x-javascript "access plus 20 weeks"
# ExpiresByType application/javascript "access plus 20 weeks"
# ExpiresByType application/x-javascript "access plus 20 weeks"
# ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
# ExpiresByType application/x-font-ttf "access plus 1 year"
# ExpiresByType application/x-font-opentype "access plus 1 year"
# ExpiresByType application/x-font-woff "access plus 1 year"
# ExpiresByType application/x-font-woff2 "access plus 1 year"
# ExpiresByType image/svg+xml "access plus 1 year"
# Options +FollowSymLinks
RewriteEngine on
rewritebase /
RewriteCond %{HTTP_USER_AGENT} libwww-perl.*
RewriteRule .* – [F,L]
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9} /index.php
RewriteRule ^index.php$ / [L,R=301]
# FOR PRODUCTION
# RewriteCond %{HTTP:X-Forwarded-Proto} !https
# RewriteCond %{HTTPS} off
# RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# RewriteCond %{HTTP_HOST} !^www. [NC]
# RewriteRule (.*) https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
ErrorDocument 404 /error-page.php
# EN
RewriteRule ^compare/?$ index.php?tag=compare [NC]
RewriteRule ^compare/([^/.]+)/?$ seo.php?tag=compare&url=$1 [NC]
RewriteRule ^compare/([^/.]+)/open?$ seo.php?tag=compare&open=true&url=$1 [NC]
RewriteRule ^compare-prices/?$ onclick-form.php?url=compare-prices [NC]
RewriteRule ^compare-prices/([^/.]+)?$ onclick-form.php?url=compare-prices&type=$1 [NC]
RewriteRule ^article/([^/.]+)/?$ artikel.php?url=$1 [NC]
RewriteRule ^articles/?$ artikelen.php [NC]
RewriteRule ^general/?$ algemeen.php?url=general [NC]
RewriteRule ^general/open/?$ algemeen.php?url=general [NC]
RewriteRule ^application/?$ algemeen.php?url=general [NC]
RewriteRule ^contact/thank-you/?$ page.php?url=thank-you [NC]
# NL and BE
RewriteRule ^vergelijk/?$ index.php?tag=compare [NC]
RewriteRule ^vergelijk/([^/.]+)/?$ seo.php?tag=compare&url=$1 [NC]
RewriteRule ^vergelijk/([^/.]+)/open?$ seo.php?tag=compare&open=true&url=$1 [NC]
RewriteRule ^prijzen-vergelijken/([^/.]+)/?$ onclick-form.php?url=prijzen-vergelijken&type=$1 [NC]
RewriteRule ^prijzen-vergelijken/?$ onclick-form.php?url=prijzen-vergelijken [NC]
RewriteRule ^artikel/([^/.]+)/?$ artikel.php?url=$1 [NC]
RewriteRule ^artikelen/?$ artikelen.php [NC]
RewriteRule ^algemeen/?$ algemeen.php?url=algemeen [NC]
RewriteRule ^algemeen/open?$ algemeen.php?url=algemeen [NC]
RewriteRule ^aanvraag/?$ algemeen.php?url=algemeen [NC]
RewriteRule ^contact/bedankt/?$ page.php?url=bedankt [NC]
Redirect /algemene-voorwaarden/ /algemene-voorwaarden-Liftoffertes.pdf
# FR
RewriteRule ^fr/comparer/([^/.]+)/?$ onclick-form.php?url=comparer&type=$1 [NC]
RewriteRule ^fr/comparer/?$ onclick-form.php?url=comparer [NC]
RewriteRule ^fr/comparer/?$ index.php?lang_code=fr&tag=compare [NC]
RewriteRule ^fr/comparer/([^/.]+)/?$ seo.php?lang_code=fr&tag=compare&url=$1 [NC]
RewriteRule ^fr/comparer/([^/.]+)/open?$ seo.php?lang_code=fr&tag=compare&open=true&url=$1 [NC]
RewriteRule ^comparer-les-prix/?$ onclick-form.php?url=comparer-les-prix [NC]
RewriteRule ^fr/article/([^/.]+)/?$ artikel.php?lang_code=fr&url=$1 [NC]
RewriteRule ^fr/articles/?$ artikelen.php?lang_code=fr& [NC]
RewriteRule ^fr/generale/?$ algemeen.php?lang_code=fr&url=generale [NC]
RewriteRule ^fr/generale/open/?$ algemeen.php?lang_code=fr&url=generale [NC]
RewriteRule ^fr/application/?$ algemeen.php?lang_code=fr&url=generale [NC]
RewriteRule ^fr/contact/merci/?$ page.php?lang_code=fr&url=merci [NC]
RewriteRule ^fr/plan-du-site/?$ sitemap.php?lang_code=fr&url=plan-du-site [NC]
RewriteRule ^fr/([^/.]+)/?$ seo.php?url=$1&lang_code=fr [NC]
RewriteRule ^fr/$ index.php?lang_code=fr [L,QSA]
RewriteRule ^fr$ index.php?lang_code=fr [L,QSA]
RewriteRule ^fr/([^/.]+).html/?$ /index.php?lang_code=fr [L,QSA]
# EN, NL, BE and FR
RewriteRule ^contact/?$ contact.php?url=contact [NC]
RewriteRule ^contact/form/?$ contact.php?url=contact [NC]
RewriteRule ^sitemap.xml?$ sitemapxml.php [NC]
# EN, NL and BE
RewriteRule ^sitemap/?$ sitemap.php?url=sitemap [NC]
# ALL
RewriteRule ^admin/$ admin/index.php [L,QSA]
RewriteRule ^/$ index.php [L,QSA]
RewriteRule ^$ index.php [L,QSA]
RewriteRule ^([^/.]+).html/?$ /index.php [L,QSA]
RewriteRule ^([^/.]+)/?$ seo.php?url=$1 [NC]
Redirect /backoffice /backoffice/pages/index.php
RewriteRule ^backoffice/?$ backoffice/pages/index.php [NC]
#
# order allow,deny
# deny from all
#
# Options All -Indexes
—
## โ Problem Summary
Your `.htaccess` is **correctly structured** with detailed rewrite rules, but **every URL redirects to the homepage** โ likely caused by one or more of these:
—
### ๐ Issue 1: Missing RewriteBase Handling
Even though you have `rewritebase /`, it's lowercase:
rewritebase /
โ **Fix:** It must be **`RewriteBase`** (case-sensitive):
RewriteBase /
—
### ๐ Issue 2: Apacheโs `mod_rewrite` might not be enabled in DDEV
Run this inside your container to confirm if `mod_rewrite` is active:
ddev ssh
apache2ctl -M | grep rewrite
You should see:
rewrite_module (shared)
โ If not present:
a2enmod rewrite
service apache2 restart
But this should already be enabled in DDEV. Still worth checking.
—
### ๐ Issue 3: Apache might not be honoring `.htaccess` due to `AllowOverride` settings
This usually isnโt an issue in DDEV, but you can force it by adding this to your `src/.htaccess` **top**:
<IfModule mod_rewrite.c>
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
</IfModule>
—
### ๐ Issue 4: Your router files (`index.php`, `seo.php`, etc.) might default to homepage output
If `.htaccess` works but `index.php` and `seo.php` route all requests to the homepage content (even 404s), then the problem is inside your PHP scripts โ not the `.htaccess`.
โ **Check `index.php` and `seo.php` logic**, make sure:
– `$_GET['url']` is actually used
– No fallback like `include('home.php');` on every request
– You log or debug the incoming URL
—
### โ Recommended Debug Step
Add this at the **very top of your `index.php`** temporarily:
echo '<pre>';
print_r($_SERVER['REQUEST_URI']);
print_r($_GET);
exit;
Then visit:
http://yoursite.ddev.site/article/test-article
Expected:
/article/test-article
Array
(
[url] => test-article
)
If nothing shows up or you always land at `/`, then either:
– Rewrite rules arenโt being processed
– PHP routing code isnโt checking `$_GET['url']` properly
—
## โ Final Checklist
| Step | โ
Done? |
|—————————–|———-|
| `RewriteBase` is capitalized| โ๏ธ |
| Apache is using `.htaccess` | โ๏ธ |
| Apache rewrite module is on | โ๏ธ |
| PHP router files check `$_GET['url']`| โ๏ธ |
| DDEV `docroot: src` | โ๏ธ |
| DDEV webserver is Apache | โ๏ธ |
| `index.php` doesn't override logic| โ๏ธ |
—
If you'd like, share the content of your `src/index.php` and `seo.php` โ I can verify that the routing logic handles SEO URLs correctly.
Let me know what you find after testing the `$_GET` and `$_SERVER['REQUEST_URI']`.
—
### โ Core Problem
Your `index.php` **does not contain any logic to handle routing based on the URL path** like `/compare/test-item` or `/article/xyz`.
Instead, it only looks for a `GET` parameter called `tag`:
$tag = $f->getValue("tag");
That means, **if someone visits**:
/compare/test-product
…it will reach `index.php`, but unless `.htaccess` rewrites that to something like:
index.php?tag=compare&url=test-product
your `index.php` will just show the homepage, because it assumes it's a default visit.
—
### โ Whatโs Missing
You're relying on these `.htaccess` rewrite rules:
RewriteRule ^compare/([^/.]+)/?$ seo.php?tag=compare&url=$1 [NC]
So this URL:
/compare/some-product
should land in `seo.php`, not `index.php`.
But your `index.php` is still being triggered โ meaning either:
—
## ๐ ๏ธ Fix 1: `.htaccess` isn't being executed at all
To test:
### โค Add this temporarily to the top of `src/.htaccess`:
RewriteRule ^debug-test$ /test-debug.php [L]
Then create a `src/test-debug.php` file:
<?php echo "REWRITE WORKS"; ?>
Visit:
http://yoursite.ddev.site/debug-test
#### โ
If you see โREWRITE WORKSโ:
โ `.htaccess` **is working**
#### โ If it still shows homepage:
โ `.htaccess` is **being ignored**
Then check again:
– Apache is used (`webserver_type: apache-fpm`)
– `.htaccess` contains `RewriteEngine On` and `RewriteBase /`
– Try adding “ wrapper
– Restart DDEV: `ddev restart`
—
## ๐ ๏ธ Fix 2: `seo.php` is not routing correctly
If `.htaccess` works but `/compare/product` still shows home, your `seo.php` must be failing or redirecting to home.
Add this to the top of `seo.php` for testing:
<?php
echo '<pre>';
print_r($_GET);
exit;
Now open:
/compare/test-item
You should see:
Array
(
[tag] => compare
[url] => test-item
)
If not, routing isnโt working or rewrite rule is broken.
—
## โ Final Suggestion
If `.htaccess` fails and youโre on Apache:
Add this near the top of `.htaccess` to handle "all" requests as fallback:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]
And then update `index.php` to handle SEO-friendly paths using:
$requestPath = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');
// route based on $requestPath
if (strpos($requestPath, 'compare/') === 0) {
include 'seo.php';
exit;
} elseif ($requestPath === 'about') {
include 'page-about.php';
exit;
}
// else show home
—
Let me know:
– What the debug output shows for `/compare/xyz`
– If `.htaccess` is firing based on the test above
And Iโll help finalize the fix.
Letโs fix that quickly.
—
## โ Step-by-Step Fix for `.htaccess` Ignored in DDEV
### ๐ง 1. **Ensure DDEV uses Apache**
Open `.ddev/config.yaml` and confirm:
webserver_type: apache-fpm
If it says `nginx-fpm`, change it to `apache-fpm`.
Then run:
ddev restart
—
### ๐ง 2. **Test if `.htaccess` is working**
#### Add this to top of your `src/.htaccess`:
RewriteEngine On
RewriteBase /
RewriteRule ^debug-check$ debug-check.php [L]
#### Create `src/debug-check.php`:
<?php echo "โ
.htaccess is working"; ?>
Now go to:
http://yourproject.ddev.site/debug-check
#### โค Results:
– โ
If it shows โ.htaccess is workingโ โ `.htaccess` is now working.
– โ If it still redirects to home โ proceed to next step.
—
### ๐ง 3. **Enable `.htaccess` in Apache config explicitly (if needed)**
Usually not needed in DDEV, but just in case:
#### Create or edit `.ddev/apache/apache-site.conf` (create folder if not exists)
mkdir -p .ddev/apache
Then create/edit `.ddev/apache/apache-site.conf`:
<Directory "/var/www/html">
AllowOverride All
Require all granted
</Directory>
Then edit `.ddev/config.yaml` to include this:
web_environment: []
additional_hostnames: []
additional_fqdns: []
use_dns_when_possible: true
webserver_type: apache-fpm
router_http_port: "80"
router_https_port: "443"
timezone: "UTC"
hooks:
post-start:
- exec: "apachectl restart"
nginx_config: []
apache_config: ["apache/apache-site.conf"]
Then restart DDEV:
ddev restart
—
### โ 4. **Try the test URL again**
Go to:
http://yourproject.ddev.site/debug-check
If you now see the output, `.htaccess` is finally working.
—
### โ After That: Restore Your Rewrite Rules
Put back your main rewrite rules into `.htaccess` and test your SEO URLs like `/compare/something`.
Let me know if the test output still fails โ Iโll help troubleshoot the exact Apache config if needed.
—
## โ Fix: Switch DDEV to Apache to Enable `.htaccess` Support
Follow these steps to switch to Apache and get your SEO URLs working:
—
### ๐ง 1. **Update `.ddev/config.yaml`**
Edit `.ddev/config.yaml`:
webserver_type: apache-fpm
Save the file.
—
### ๐ง 2. **Restart DDEV**
Run this in your project root:
ddev restart
—
### ๐ง 3. **Verify Apache is Active**
Run:
ddev describe
Look for:
webserver_type: apache-fpm
—
### ๐ง 4. **Test `.htaccess` is Working**
Add to top of your `src/.htaccess`:
RewriteEngine On
RewriteRule ^htaccess-test$ htaccess-test.php [L]
Create `src/htaccess-test.php`:
<?php echo "โ
.htaccess is working!"; ?>
Now visit:
http://yourproject.ddev.site/htaccess-test
If you see the message โ `.htaccess` is now working โ
—
### โ Finally: Restore Your Full Rewrite Rules
Put back your original `.htaccess` content (the one you shared earlier), and now your SEO URLs like `/compare/xyz`, `/artikel/test`, etc., should work correctly.
—
Let me know once you've switched and tested โ Iโll help debug further if needed.
404 page not found
Letโs finish fixing it.
—
## โ Goal
If a URL like `/compare/something` is entered and no physical file exists, it should rewrite to a PHP file (like `seo.php` or `index.php`) โ not 404.
—
## ๐ง 1. **Ensure `.htaccess` has fallback rules**
At the **end of your `.htaccess`**, add a **catch-all** rewrite rule:
# Catch-all fallback for URLs not matched above
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/.]+)/?$ seo.php?url=$1 [QSA,L]
> This sends any unmatched URL (e.g., `/foo`) to `seo.php?url=foo`
—
## ๐ง 2. **Double-check RewriteBase and engine are on**
Make sure you have **at the top** of `.htaccess`:
RewriteEngine On
RewriteBase /
—
## ๐ง 3. **Test with debug output**
Temporarily edit `seo.php` like this at the top:
<?php
echo '<pre>';
print_r($_GET);
exit;
Visit:
http://yourproject.ddev.site/compare/something
Expected Output:
Array
(
[tag] => compare
[url] => something
)
If this appears โ the rewrites work and now your PHP routing logic needs to show content.
—
## โ Optional: Send all unmatched to `index.php`
If you want **all unmatched URLs** to be handled inside `index.php` instead of `seo.php`, then use this instead:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]
Then in `index.php`, inspect `$_SERVER['REQUEST_URI']` or use a router.
—
## ๐ Summary
| Task | Status |
|——|——–|
| Switched from nginx to apache-fpm | โ
|
| `.htaccess` now working | โ
|
| 404 on SEO URLs | ๐ง fixing now |
| Add fallback rewrite rule | โ
|
| Route inside `seo.php` or `index.php` | ๐ You choose |
—
Let me know whether you want all traffic routed through `seo.php` or `index.php`, and Iโll finalize the routing logic with you.