首页 > 解决方案 > 如何将简单的 index.php 重定向从 apache 转换为 nginx 格式?

问题描述

从一个旧项目中,我得到了这个 .htaccess 文件,它当前处理 apache 重写规则:

<IfModule mod_rewrite.c>
    RewriteEngine On
    # Folders / files to exclude from rewrite divided by Pipe goes here:
    RewriteRule (^|/)install(/|$) - [L,NC]
    RewriteRule (^|/)web(/|$) - [L,NC]

    # turn empty requests into requests for "index.php",
    # keeping the query string intact
    RewriteRule ^$ index.php [QSA]

    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !favicon.ico$
    RewriteRule ^(.+)$ index.php [QSA,L]

    RewriteRule ^(.+)$ index.php [QSA,L]
</IfModule>

上面的代码片段基本上做了什么,它通过保持查询部分字符串完整并在第一次匹配后停止来重写对 index.php 的所有请求,除非文件或文件夹存在。

因此,总而言之,通过查阅 Nginx 文档并添加一些额外的安全性(允许访问合理的文件夹和文件)和与性能相关的东西(在可能的情况下启用 gzip 或 brotli 输出压缩[取决于客户端,它是哪一个)能够处理]),我得到了这个:

server {
    listen 80;
    listen [::]:80;
    server_name subdomain.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name tdc.wunner-software.de;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Add headers to serve security-related headers
    # Before enabling Strict-Transport-Security headers please read into this
    # topic first.
    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
    #
    # WARNING: Only add the preload option once you read about
    # the consequences in https://hstspreload.org/. This option
    # will add the domain to a hardcoded list that is shipped
    # in all major browsers and getting removed from this list
    # could take several months.
    add_header Referrer-Policy "no-referrer" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Download-Options "noopen" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    add_header X-Robots-Tag "none" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # Remove X-Powered-By, which is an information leak
    fastcgi_hide_header X-Powered-By;

    # Path to the root of your installation
    root /srv/www/vhosts/example.com/subdomain.example.com;

    index index.php index.html;

    charset utf-8;

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # set max upload size
    client_max_body_size 512M;
    fastcgi_buffers 64 4K;

    # Enable gzip but do not remove ETag headers
    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    brotli on;
    brotli_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    # Adding the cache control header for js, css and map files
    # Make sure it is BELOW the PHP block
    location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
        try_files $uri /index.php$request_uri;
        add_header Cache-Control "public, max-age=15778463";
        # Add headers to serve security related headers (It is intended to
        # have those duplicated to the ones above)
        # Before enabling Strict-Transport-Security headers please read into
        # this topic first.
        add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;

        # Optional: Don't log access to assets
        access_log off;
    }

    location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap|mp4|webm)$ {
        try_files $uri /index.php$request_uri;
        # Optional: Don't log access to other assets
        access_log off;
    }

    location ~ ^/vendor/.*$ {
        deny all;
    }

    location ~ ^\.htaccess$ {
        deny all;
    }

    #rewrite ^(.*)/$ index.php$is_args$args break;

    location ~ /(.*)$ {
        index index.php;
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        include fastcgi.conf;
        fastcgi_param HTTP_PROXY "";
        fastcgi_param HTTPS $https;
        fastcgi_buffers 8 16k;
        fastcgi_buffer_size 32k;
        fastcgi_read_timeout 300s;
        client_body_buffer_size 128k;
        fastcgi_pass php-handler;
        http2_push_preload on;
    }

    access_log /var/log/nginx/subdomain_example_com_ccess.log;
    error_log /var/log/nginx/subdomain_example_com_error.log;
}

上面的新 Nginx 配置下载 PHP 文件,而不是将其绕过到 FPM 处理程序。我发现了这个这个SO 问题,但它似乎对我没有用。此外,正如 Nginx 文档中所述,if它是邪恶的,除非我们 100% 确定发生了什么,否则我们应该避免使用它。因此,对于我的配置,我避免使用它。主要是因为 regex 语法也适用于这种情况,location并且在这种情况下比if. 我的意思是,据我所知。请纠正我,如果我错了。

nginx.conf文件现在非常简单:

user  nginx;
worker_processes  1;

# load_module lib64/nginx/modules/ngx_http_fancyindex_module.so;
# load_module lib64/nginx/modules/ngx_http_geoip_module.so;
# load_module lib64/nginx/modules/ngx_http_headers_more_filter_module.so;
# load_module lib64/nginx/modules/ngx_http_image_filter_module.so;
# load_module lib64/nginx/modules/ngx_http_perl_module.so;
# load_module lib64/nginx/modules/ngx_http_xslt_filter_module.so;
# load_module lib64/nginx/modules/ngx_mail_module.so;
# load_module lib64/nginx/modules/ngx_rtmp_module.so;
# load_module lib64/nginx/modules/ngx_stream_geoip_module.so;
# load_module lib64/nginx/modules/ngx_stream_module.so;

error_log  /var/log/nginx/error.log;
#error_log  /var/log/nginx/error.log  notice;
error_log  /var/log/nginx/error.log  info;

#pid        /run/nginx.pid;

events {
    worker_connections  1024;
    use epoll;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    gzip  on;

    include conf.d/*.conf;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  /var/log/nginx/host.access.log  main;

        location / {
            root   /srv/www/htdocs/;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /srv/www/htdocs/;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        location ~ \.php$ {
            root           /srv/www/htdocs/;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
            include        fastcgi_params;
        }

        # deny access to .htaccess files if Apache's document root
        # concurs with Nginx's one
        #
        location ~ /\.ht {
            deny  all;
        }
    }


    # another virtual host using a mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   /srv/www/htdocs/;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;
    
    #    Allow TLS version 1.2 only, which is a recommended default these days
    #    by international information security standards.
    #    ssl_protocols        TLSv1.2;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   /srv/www/htdocs/;
    #        index  index.html index.htm;
    #    }
    #}

    include vhosts.d/*.conf;
}

上面的文件主要是 openSUSE 发行商自带的文件,有一些改动。

这是我的base.conf文件,它定义了 nginx 的 FPM 代理处理程序(为了不重复自己;无论如何它都是必需的):

upstream php-handler {
    server 127.0.0.1:9000;
    #server unix:/var/run/php/php7.2-fpm.sock;
}

我将不胜感激有关该问题的任何帮助。

标签: phpnginxmod-rewriteurl-rewriting

解决方案


上面的代码片段基本上做了什么

您可能需要一些时间来考虑这些规则所做的不太明显的事情。

上面的新 Nginx 配置下载 PHP 文件而不是绕过它到 FPM 处理程序

因为您对 php 脚本的所有引用都被拦截,location ~ \.(?:css|js|woff2?|svg|gif|map)$因此不会将工作移交给 PHP FPM。了解 nginx 考虑 location{} 块的优先级对于实现成功的配置至关重要。

您的 try_files 指令似乎适用于整个服务器,但您已将其放在位置块中。它应该在服务器块中,以便在nginx 尝试匹配位置块之前应用它。

使用虚拟(或“命名”)位置块提供了一种处理默认选项具有处理程序(如 PHP)的情况的方法。请参阅try_files 文档中的Drupal/Fastcgi 示例。

你的配置应该看起来像

server {
...
try_files $uri @phpfpm;

location ~ \.(css|js|woff2?|svg|gif|map)$ {
        add_header Cache-Control "public, max-age=15778463";
        add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
        access_log off;
}
location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        include fastcgi.conf;
        fastcgi_param HTTP_PROXY "";
        fastcgi_param HTTPS $https;
        fastcgi_buffers 8 16k;
        fastcgi_buffer_size 32k;
        fastcgi_read_timeout 300s;
        client_body_buffer_size 128k;
        fastcgi_pass php-handler;
        http2_push_preload on;
}
location @phpfpm {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        include fastcgi.conf;
        fastcgi_param HTTP_PROXY "";
        fastcgi_param HTTPS $https;
        fastcgi_buffers 8 16k;
        fastcgi_buffer_size 32k;
        fastcgi_read_timeout 300s;
        client_body_buffer_size 128k;
        fastcgi_pass php-handler;
        http2_push_preload on;
}

虽然你真的应该将所有 PHP 配置打包在一个单独的文件中。此外,您似乎有点担心获得良好的性能,但使用的是与本地主机的 TCP 套接字连接而不是文件系统套接字——这会影响性能和容量。


推荐阅读