nginx+apache+单页面刷新404问题解决方案

0. 背景

一个实际的应用环境中需要同时部署多个站点,所以选择同时使用nginx和apache,使用apache web容器同时部署2个站点,使用nginx进行转发。但由于网站是使用react或vue开发的,一个使用了hash router,一个使用的是history router,中途遇到一个问题是当使用的是history路由时,如果当前而在不是index页面,浏览器刷新后会出现404错误

1. 应用运行环境

操作系统:CentOS Linux release 7.6.1810 (Core)

Web容器:Apache/2.4.6 (CentOS)

nginx:nginx/1.16.0

2. Apache多站点部署快捷方法

方法一:同域名下多个站点

本质上仍是一个网站,通过二级目录的方式划分成2个不同的站点

方法二:不同域名多个站点

本质是是通过Apache的VirtualHost实现,具体配置详细说明如下:

由于在我的应用环境中向外是通过nginx做转发,所以这里说明通过VirtualHost基本端口号实现多站点的配置过程

  • 在/etc/httpd/conf/httpd.conf中增加以下内容

    Listen 8080
    Listen 8090
    #以上将在VirtualHost配置中使用
    LoadModule rewrite_module modules/mod_rewrite.so
    #上一行配置的作用在后续解决刷新404问题中的一步
    
  • 在/etc/httpd/conf.d/下新建host.conf文件,文件在配置以下内容

    
            ServerAdmin root@localhost
            ServerName localhost:8091
            DocumentRoot "网站A根目录"
            DirectoryIndex index.html
            
                    Options Indexes Includes FollowSymlinks
                    AllowOverride All
                    Require all granted
            
            ErrorLog /var/log/httpd/err_8080.log
            LogLevel warn
            CustomLog /var/log/httpd/err_8080_access.log combined
    
    
            ServerAdmin root@localhost
            ServerName localhost:8090
            DocumentRoot "网站B根目录"
            DirectoryIndex index.html
            
                    Options Indexes Includes FollowSymlinks
                    AllowOverride All
                    Require all granted
            
            ErrorLog /var/log/httpd/err_8090.log
            LogLevel warn
            CustomLog /var/log/httpd/err_8090_access.log combined
    
    

3. nginx转发配置

这里假设A网站分配域名为a.me.com,B网站分配域名是b.me.com

详细配置如下:

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    #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  logs/access.log  main;

    sendfile        on;
    underscores_in_headers on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;
   #将http请求转发到https
   server {
        listen 80;
        server_name a.me.com;
        return 301 https://$server_name$request_uri;
   }

   server {
        listen 80;
        server_name b.me.com;
        return 301 https://$server_name$request_uri;
   }
    # HTTPS server
    #
    server {
        listen 443 ssl;
        server_name a.me.com;
        root html;
        index index.html index.htm;
        ssl_certificate   对应域名ssl证书路径;
        ssl_certificate_key  对应域名ssl证书key路径;
        ssl_session_timeout 5m;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        location ~* / {
            client_max_body_size  100M;
            proxy_pass http://localhost:8080;
            add_header Cache-Control 'max-age=0';
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
        location / {
            root html;
            index index.html index.htm;
        }
    }


    server {
        listen 443 ssl;
        server_name b.me.com;
        root html;
        index index.html index.htm;
        ssl_certificate   对应域名ssl证书路径;
        ssl_certificate_key  对应域名ssl证书key路径;
        ssl_session_timeout 5m;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        location ^~ /h5/ {
            proxy_pass http://localhost:8090;
            add_header Cache-Control 'max-age=0';
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        location ~* / {
            client_max_body_size  100M;
            proxy_pass http://localhost:8090;
            add_header Cache-Control 'max-age=0';
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        location / {
            client_max_body_size  100M;
            root html;
            index index.html index.htm;
        }
    }
}

4.怎么解决问题

在上述配置下使用React或Vue基本history的单页面路由,当当前页面不是index页面时刷新时出现404问题

  • 定位产生问题原因

    由于同时使用了nginx和apache,如果不确定问题是出在谁身上只能一点点的去试,显然是没办法的办法。其实从本质上分析nginx是做转发工作,那只要确定问题是不是出在转发层面就可以了。方法很直接:

    由于服务并没有向外开放8080和8090端口,所以可以直接在服务上通过wget打开一个非index的具体页面,如果可以打开那么问题自然是在nginx上,否则是apache配置的问题。

    经排除发现问题是在apache端

  • 问题解决办法

    第一步、在apache配置文件中增加以下配置

    LoadModule rewrite_module modules/mod_rewrite.so
    

    第二步、在网站根目录下新建.htaccess,文件内容如下

    
      RewriteEngine On
      RewriteBase /
      RewriteRule ^index\.html$ - [L]
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteRule . /index.html [L]
    
    

你可能感兴趣的:(nginx+apache+单页面刷新404问题解决方案)