Search code examples
regex.htaccessnginxurl-rewriting

Nginx rewrite urls as index.php parameters


I've recently switched from apache to nginx. My app is located under /var/www/html and has the following structure:

# tree -a /var/www/html
/var/www/html
├── .htaccess
├── folder1
│   ├── .htaccess
│   └── index.php
├── folder2
│   ├── .htaccess
│   └── index.php
├── index.php
├── file1.php
└── file2.php

.htaccess files in all directories are the same:

# cat /var/www/html/.htaccess 
AddDefaultCharset UTF-8
Options +FollowSymLinks
Options -Indexes
RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(assets)/ - [L,NE]

RewriteRule ^/?$ index.php?param1=main [QSA,L]
RewriteRule ^([-a-zA-Z0-9_]+)/$ index.php?param1=$1 [QSA,L]
RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/$ index.php?param1=$1&param2=$2 [QSA,L]
RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/$ index.php?param1=$1&param2=$2&param3=$3 [QSA,L]
RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/$ index.php?param1=$1&param2=$2&param3=$3&param4=$4 [QSA,L]

RewriteRule ^file1.xml$ file1.php [L]
RewriteRule ^file2.xml$ file2.php [L]

ErrorDocument 400 /error/400/
ErrorDocument 401 /error/401/
ErrorDocument 403 /error/403/
ErrorDocument 404 /error/404/
ErrorDocument 500 /error/500/

RedirectMatch (.*)\.inc /error/404/
RedirectMatch (.*)\.tpl /error/404/

Now I am trying to build the nginx config. My goal is to make the following rewrites:

example.com or example.com/ -> example.com/index.php?param1=main
example.com/anything or example.com/anything/ -> example.com/index.php?param1=anything
example.com/anything/else or example.com/anything/else/ -> example.com/index.php?param1=anything&param2=else
etc...

And the same for folder1 and folder2

example.com/folder1 or example.com/folder1/ -> example.com/folder1/index.php?param1=main
example.com/folder1/anything or example.com/folder1/anything/ -> example.com/folder1/index.php?param1=anything
etc...

Nginx config currently looks like:

server {
    listen 443 ssl;
    server_name example.com;
    charset utf-8;

    ssl_certificate /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!MD5;

    root /var/www/html;
        index index.html index.htm index.php;

    location / {
    try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    }

    location ~ /\.ht {
        deny all;
    }
} 

I used online converter and placed rules under location / { but that did not work. Url like example.com/anything shows 404 and example.com/anything/ tries to download file


Solution

  • There is more than one way to do this. But the following example uses a named location to collect together all of the rewrite rules.

    location / {
        try_files $uri $uri/ @rewrite;
    }
    location @rewrite {
        rewrite ^/$ /index.php?param1=main? last;
        rewrite ^/([-a-zA-Z0-9_]+)/$ /index.php?param1=$1? last;
        rewrite ^/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/$ /index.php?param1=$1&param2=$2? last;
        rewrite ^/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/$ /index.php?param1=$1&param2=$2&param3=$3? last;
        rewrite ^/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/$ /index.php?param1=$1&param2=$2&param3=$3&param4=$4? last;
    
        rewrite ^/file1.xml$ /file1.php last;
        rewrite ^/file2.xml$ /file2.php last;
        return 404;
    }
    

    All Nginx URIs begin with a leading /. I am assuming that the default action is to return 404 if none of the rules match. The trailing ? prevents any original parameters being appended to the rewritten URI, if you want the original parameters appended, remove the trailing ?. See this document for details.

    Note: In your question you state that you want to accept example.com/anything/else or example.com/anything/else/. This is not what the existing .htaccess does. To make the trailing / optional, add a ? after the final / before the $. For example:

    rewrite ^/([-a-zA-Z0-9_]+)/?$ /index.php?param1=$1? last;
    

    You can repeat this pattern for other folders, but it will look very messy. This logic belongs inside index.php, but an Nginx example is given below:

    location /folder1 {
        try_files $uri $uri/ @rewrite1;
    }
    location @rewrite1 {
        rewrite ^/folder1/?$ /folder1/index.php?param1=main? last;
        rewrite ^/folder1/([-a-zA-Z0-9_]+)/$ /folder1/index.php?param1=$1? last;
        ...
    }
    

    Notice that the regular expression and the rewritten URI both need to include the folder name.