私有云NAS部署网站及公网挑战

如果您也是计划本地部署网站,通过DDNS公网能够访问80/443网站的话,且不打算采用内网穿透或其他的方案的话,建议直接看第五章
重要优先看:公网挑战及放弃本地部署网站

1. NAS配置及代码迁移

极空间Z4Pro性能版,8核16G内存; 。
方案一: Docker部署 ;方案二:虚拟机方案;

  • 方案选择: 因为本人没买m.2固态硬盘,所以没有办法运行虚机; 直接采用docker容器方案;全部轻量化部署

    以上是 NAS容器中部署的三个docker镜像。当然也可以直接找个 LNMP的合成Docker镜像进行运行;这里为了以后独立升级,轻量化和解耦,采用三个独立的容器运行。其中每个容器都采用host主机模式网络进行部署,公用同一个网络环境。

1.1. php-fpm-docker

  • 在部署php-fpm的容器时,因为wordpress需要访问mysql, 这里自行安装了php-mysqli相关组件;

目前该容器已推送官方镜像,通过如下命令下载 docker push turbock/custom-php-fpm:latest

FROM php:8.3.17RC1-fpm-alpine3.21
# php:8.3.16-fpm-alpine3.20

# 安装依赖并启用mysqli
RUN apk update && \
    apk add --no-cache mysql-client && \
    docker-php-ext-install mysqli && \
    docker-php-ext-enable mysqli

apt install -y procps vim netools telnet iputils-ping
# 挂载项目代码(假设项目目录为src)
WORKDIR /usr/share/nginx/html
COPY ./src /usr/share/nginx/html/

# # 查看已加载的PHP模块
# php -m | grep mysqli

# # 或检查phpinfo()
# php -r 'phpinfo();' | grep mysqli

# apt install -y php-fpm
# php --version
# apt install -y php8.2-*
# apt install -y php8.2-fpm php8.2-opcache php8.2-cli php8.2-gd php8.2-imap php8.2-mysqlnd php8.2-mbstring php8.2-mcrypt php8.2-pdo php8.2-pecl-* php8.2-xml
# apt remove -y php-yac php-apcu php-imagick
# apt remove -y php8.2-imagick php8.2-apcu

alpine系统,采用apk方式进行安装

docker tag custom-php-fpm turbock/custom-php-fpm:v1.1
docker tag custom-php-fpm turbock/custom-php-fpm:latest
docker push turbock/custom-php-fpm:latest

/usr/local/bin/php
/usr/local/sbin/php-fpm
/usr/local/etc/php-fpm.conf

/usr/local/etc # tree
.
├── pear.conf
├── php
│   ├── conf.d
│   │   ├── docker-fpm.ini
│   │   └── docker-php-ext-sodium.ini
│   ├── php.ini-development
│   └── php.ini-production
├── php-fpm.conf
├── php-fpm.conf.default
└── php-fpm.d
    ├── docker.conf
    ├── www.conf
    ├── www.conf.default
    └── zz-docker.conf

1.2. mysql docker

配置环境变量
MYSQL_ROOT_PASSWORD 为自己的mysql 登录密码, 用户名默认root

mysql -h localhost -u root -p
Mysqlturbock79;
SHOW GRANTS FOR 'root'@'%';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;

//查询端口是否发布
microdnf --version
microdnf update
microdnf install net-tools
netstat -tunlp | grep 3306

2. 公网EIP及DDNS

2.1. 入户公网IP查询

  • 家庭网络POC验证
    查询本地网线入户路由器的wan口地址和实际的外网地址端口是否一致,这里可以用网址https://www.ip38.com/ 查询本地实际公网ip;

接入网络采用电信网络, 目前因电信不采用公网IP动态分配,而是做了一层nat, 导致入户网络没有公网。 无论如何使用DDNS,都无法实现 域名到 DDNS的动态转换。因此计划采用公网EIP直接做端口映射;

  • 打电话沟通运营商,申请公网EIP;
  • 如果您家庭路由器性能较好, 可以将 宽带运营商的 入户路由器 纯作为光猫使用(光信号和网络信号互转),然后采用桥接模式;然后在家庭路由器上进行拨号上网;

2.2. DDNS 地址动态转换

  • 通过添加10元改为获取动态IP后, 每天的公网EIP都是变化的,这里就需要启用NAS或如何核心路由器上的DDNS功能了;
    DDNS主要是将入户的动态公网IP上传到域名解析地址服务器,即使公网IP变化也能实现域名到IP的正确解析;

  • DDNS调研
    ddns主要有两种方案,一种是家庭路由器直接配置ddns,另一个是 NAS/主机上安装ddns客户端进行动态检查ip并上报;
    本人这里主要还是看家庭的路由器支持的ddns厂商都有哪些,然后针对性的研究ddns厂商设置;因为家里使用核心路由器的DDNS支持厂商我这里都没有申请过,花生壳还需要重新做认证和域名转入。所以计划采用极空间内置的DDNS;

  • 因采用极空间,内置DDNS的模块进行解析;

  • 因为本人原有使用的就是DNSPod解析的,这里还是使用腾讯云的DNSPod进行动态解析;
    获取密钥地址API 密钥 - DNSPod-免费智能DNS解析服务商


  • 要注意的是,DNSPod Token 而不是腾讯云账号的API

2.3. 域名内部地址映射

  • 在路由器上(宽带运营商入户路由器或 个人路由器)进行端口地址转换
    网络架构(公网 80/443 端口映射到内网 8880/8443)

3. 内外网重定向统一入口

3.1. 问题描述

现象:
网络架构(公网 80/443 端口映射到内网 8880/8443);nas上部署wordpress, 私网地址是192.168.31.182:8880, 公网地址是 111.111.111.111, 在路由器上做了端口映射 内网8880转公网的80端口, 但是wordpress 因为内部配置了siteurl,默认用80端口, 会不断的改目的端口,导致只能公网访问。私网192.168.31.182:8880 无法访问;

分析:WordPress 的 siteurl 和 home 配置强制指向公网地址 111.111.111.111:80,导致内网用户通过 192.168.31.182:8880 访问时会被重定向到公网地址,而内网环境无法通过公网 IP + 80 端口直接访问(需依赖路由器 NAT 回环支持)。

总结需求,即需要让 WordPress 动态适应内外网环境,或通过技术手段统一访问入口。同时,内外网都能正常访问图片和地址格式;

3.2. 解决方案

基于Nginx 配置方案,确保内外网访问兼容性、HTTPS 重定向和安全性:

3.2.1. 优化后的wordpress wp-config.php 配置

  1. 更灵活的动态配置(支持多环境)
// 在 wp-config.php 中添加(确保在 require_once(ABSPATH . 'wp-settings.php'); 之前)
$protocol = 'http://';
if (
    (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ||       // 直接 HTTPS 访问
    (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')  // 代理转发(如 Nginx + Cloudflare)
) {
    $protocol = 'https://';
}

$current_host = $_SERVER['HTTP_HOST'];
// 判断是否为内网或本地访问
if (
    strpos($current_host, '192.168.') === 0 ||  // 内网 IP 段
    strpos($current_host, '127.0.0.1') === 0 ||       // 本地回环
    strpos($current_host, 'localhost') === 0          // 本地域名
) {
    define('WP_HOME', $protocol . $current_host);
    define('WP_SITEURL', $protocol . $current_host);
} else {
    // 公网访问强制使用 HTTPS
    define('WP_HOME', 'https://turbock79.com');
    define('WP_SITEURL', 'https://turbock79.com');
}
  1. 代码说明

    • 动态协议检测:同时支持直接 HTTPS 访问和反向代理(如 Cloudflare、Nginx 负载均衡器)的 X-Forwarded-Proto 标头。
    • 公网强制 HTTPS:无论用户是否通过 HTTP 访问,公网地址始终重定向到 https://test.com
    • 动态协议:自动识别 http:// 或 https://。
    • 灵活匹配:匹配所有 192.168.x.x 内网 IP(无需写死具体 IP)。支持 127.0.0.1:8880 和 localhost:8880。其他情况自动使用公网域名 test.com。
    • 无需硬编码:通过 $current_host 动态获取请求地址。

3.2.2. 数据库修改步骤

  1. 理想情况下不需要
    如果您的 WordPress 站点 完全使用动态配置(通过 wp-config.php 动态设置 WP_HOME 和 WP_SITEURL),并且 所有内容链接均为相对路径,则 无需修改数据库。

  2. 现实情况可能需要修改
    大多数 WordPress 站点会在数据库中硬编码链接(尤其是媒体文件、文章内部链接)。启用 HTTPS 后,需确保数据库中的链接从 http:// 更新为 https://

  3. 使用 SQL 批量替换旧链接

    • 登录数据库 windows可以使用MysqlWorkbench 或者navicat;
    • linux 执行命令mysql -h localhost -u root -p 然后输入密码

    在 WordPress 数据库执行以下 SQL(通过 phpMyAdmin 或命令行):

    -- 替换选项表中的地址
    UPDATE wp_options SET option_value = REPLACE(option_value, 'http://test.com', 'https://test.com') WHERE option_name IN ('home', 'siteurl');
    
    -- 替换文章内容中的地址
    UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://test.com', 'https://test.com');
    
    -- 替换文章元数据中的地址
    UPDATE wp_postmeta SET meta_value = REPLACE(meta_value, 'http://test.com', 'https://test.com');
    
    -- 替换修订历史中的地址(可选)
    UPDATE wp_posts SET guid = REPLACE(guid, 'http://test.com', 'https://test.com');
  4. 使用插件自动替换(推荐)

    安装插件 Better Search Replace。

    进入 Tools > Better Search Replace。

    输入替换参数:

    Search for: http://test.com

    Replace with: http://192.168.31.182:8880

    勾选所有表(wp_posts, wp_postmeta, wp_options 等)。

    执行替换前备份数据库。

3.2.3. nginx服务配置

  • 内外网请求分离:通过 server_name 区分公网域名和内网 IP 请求。
  • 公网 HTTP 强制跳转 HTTPS:公网用户访问 80 端口(内网 8880)时,自动跳转到 443 端口(内网 8443)。
  • 内网直连 HTTP:内网用户通过 192.168.31.182:8880 访问时不强制跳转 HTTPS。
  • 统一 SSL 配置:公网 HTTPS 使用标准安全配置,内网可保持 HTTP。
# ----------------------------
# 公网 HTTP 请求(外网80 → 内网8880),强制跳转 HTTPS
# ----------------------------
server {
    listen 8880;
    server_name test.com 111.111.111.111;  # 公网域名和IP

    # 301 永久重定向到 HTTPS(外网443 → 内网8443)
    return 301 https://$host$request_uri;

    access_log ./host.access.log main;
}

# ----------------------------
# 内网 HTTP 请求(不跳转 HTTPS)
# ----------------------------
server {
    listen 8880;
    server_name 192.168.31.182;  # 内网IP

    root /usr/share/nginx/html/wordpress;
    index index.php index.html index.htm;
    access_log ./host.access.log main;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }
}

# ----------------------------
# 公网 HTTPS 请求(外网443 → 内网8443)
# ----------------------------
server {
    listen 8443 ssl;
    server_name test.com 111.111.111.111;

    # SSL 证书路径
    ssl_certificate /etc/nginx/conf.d/sec/test.com_bundle.crt;
    ssl_certificate_key /etc/nginx/conf.d/sec/test.com.key;

    # SSL 安全配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers on;
    ssl_ecdh_curve secp384r1;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_buffer_size 4k;

    root /usr/share/nginx/html/wordpress;
    index index.php index.html index.htm;
    access_log ./host.access.log main;

    # 安全 HTTP 头
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    add_header X-Content-Type-Options "nosniff";
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header Referrer-Policy "strict-origin-when-cross-origin";

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    # 静态资源缓存
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff2)$ {
        expires 365d;
        add_header Cache-Control "public, no-transform";
        access_log off;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }
}

关键说明

  1. 分离公网与内网 HTTP 服务
    公网 HTTP(8880 端口):
    匹配 server_name turbock79.com 115.171.216.250,强制跳转到 HTTPS。
    内网 HTTP(8880 端口):
    匹配 server_name 192.168.31.182,直接处理请求不跳转。

  2. 公网 HTTPS 统一入口
    监听 8443 端口(映射到公网 443),处理所有 HTTPS 流量。
    启用现代 SSL 协议(TLSv1.2/1.3)和强加密套件。
    添加安全 HTTP 头,防御 XSS、点击劫持等攻击。

  3. 静态资源缓存优化
    图片、CSS、JS 等静态资源设置长期缓存,减少服务器负载。
    关闭静态资源访问日志 (access_log off)。

3.2.4. 注意事项

  1. 清除缓存
    清除浏览器缓存和 WordPress 缓存插件(如 WP Super Cache)的数据。
    刷新固定链接:进入 Settings > Permalinks > Save Changes。

  2. 验证配置

    内网访问:
    浏览器访问 http://192.168.31.182:8880,检查页面链接是否均为内网地址

    公网访问:
    通过手机网络访问 http://test.com,确保内容正常加载

3.2.5. 故障排查

  1. 检查重定向问题

    在 wp-config.php 中临时禁用重定向:

    define('WP_HOME', 'http://192.168.31.182:8880');
    define('WP_SITEURL', 'http://192.168.31.182:8880');
    // define('RELOCATE', true);  // 取消注释强制重新定位
  2. 查看当前配置

    在 WordPress 后台 Settings > General 检查 WordPress Address 和 Site Address 是否动态变化。
    通过以上优化,您的 WordPress 将自动适应内外网环境,且数据库中的旧链接会被正确替换,无需硬编码 IP!

4. 协议相对路径全局配置方案

4.1. 问题核心分析

混合协议冲突:数据库中存在 HTTP 地址,但公网强制 HTTPS 导致图片无法加载(混合内容被浏览器拦截)

端口残留问题:内网访问时可能遗留端口号,导致重定向异常

动态配置不完善:wp-config.php 的协议判断逻辑需优化

4.2. 步骤1 数据库修改

  1. 修复数据库中的混合协议内容

执行以下 SQL 替换所有 HTTP 地址为协议相对路径(同时处理内外网地址):

-- 替换所有 HTTP/HTTPS 开头的旧地址为协议相对路径
UPDATE wp_options SET option_value = REPLACE(option_value, 'http://turbock79.com', '//turbock79.com') WHERE option_name IN ('home', 'siteurl');
UPDATE wp_options SET option_value = REPLACE(option_value, 'https://turbock79.com', '//turbock79.com');

UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://192.168.31.182:8880', '//turbock79.com');
UPDATE wp_posts SET post_content = REPLACE(post_content, 'https://192.168.31.182:8880', '//turbock79.com');

UPDATE wp_posts SET guid = REPLACE(guid, 'http://192.168.31.182:8880', '//turbock79.com');
UPDATE wp_postmeta SET meta_value = REPLACE(meta_value, 'http://192.168.31.182:8880', '//turbock79.com');

4.3. 步骤2 强制WordPress使用协议相对路径

4.3.1. 方案一:使用 Must-Use 插件(推荐)

在 WordPress 目录创建新路径:

mkdir -p /path/to/wordpress/wp-content/mu-plugins

创建协议转换插件文件:

vim /path/to/wordpress/wp-content/mu-plugins/protocol-relative.php

文件内容如下(自动对所有输出内容生效):

<?php
// 全局协议相对路径转换
function global_protocol_relative($content) {
    $domain = 'turbock79.com'; // 你的公网域名

    // 替换所有绝对地址为协议相对路径
    $replacements = array(
        'http://' . $domain  => '//' . $domain,
        'https://' . $domain => '//' . $domain,
        'http://192.168.31.182:8880'  => '//' . $domain,
        'https://192.168.31.182:8880' => '//' . $domain
    );

    return str_replace(array_keys($replacements), array_values($replacements), $content);
}

// 应用范围
add_filter('the_content', 'global_protocol_relative');
add_filter('post_thumbnail_html', 'global_protocol_relative');
add_filter('wp_get_attachment_url', 'global_protocol_relative');
add_filter('stylesheet_uri', 'global_protocol_relative');
add_filter('script_loader_src', 'global_protocol_relative');
add_filter('style_loader_src', 'global_protocol_relative');

4.3.2. 方案二:修改 wp-config.php(次选)

在 wp-config.php 最后添加:

// 在 require_once(ABSPATH . 'wp-settings.php'); 之后添加
add_action('init', function() {
    ob_start(function($buffer) {
        return str_replace(
            ['http://turbock79.com', 'https://turbock79.com', 'http://192.168.31.182:8880'],
            '//turbock79.com',
            $buffer
        );
    });
});

5. 重要优先看:公网挑战及放弃本地部署网站

国内环境限制,ISP运营商封堵 80和443等端口;

  • 即使我把本地DDNS的公网IP通过域名转化,也导致浏览器(默认80和443端口)无法正常访问NAS本地对应http页面。 即使各种备案手续齐全也不行,因为是动态公网IP,备案无法接受动态IP;

  • 如果想办理本地,需要本地拉取办理专线(好像最少10w),确定固定公网IP之后, 才可本地化部署网站(80/443浏览器访问)。

  • 如果只是本地自己玩,不需要公网也行。或者本地部署其他服务,类似mqtt-server 1883等端口,还是可以公网正常访问的。

  • 本人个人NAS小型化, 没必要花费较多办理专线,该NAS+DDNS+公网 方案只能放弃

  • 可以考虑内网穿透方案,类似的产品有花生壳,或frp自行搭建穿透服务;

  • 综合成本和开发部署周期,暂计划采用公有云厂商实现了, 顺便安利一下几个云厂商优惠券链接;

赞赏

微信赞赏支付宝赞赏

发表评论

邮箱地址不会被公开。 必填项已用*标注