使用 Nginx 在 Ghost 博客上进行 SSL 重定向和身份验证
SSL redirection and authentication on Ghost blog with Nginx
我在 DigitalOcean 上有一个关于 Ghost 的博客。同样由Nginx服务,配置文件如下:
server {
listen 443 ssl;
server_name www.example.com;
return 301 $scheme://example.com$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include snippets/ssl-params.conf;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:2825;
}
}
证书是使用 Let's Encrypt 生成的,适用于带 www 和不带 www 的域。
在 Ghost config.js 文件中,URL 中写入了采用 SSL 的规则:https://example.com/
问题是,当我进入我的博客时,我的域没有 wwww,正确登录并使用 SSL,但是当我尝试使用 https://www.example.com 登录时,我收到 SSL 证书身份验证错误。
我真的不明白这里可能有什么问题。
我需要在使用 www 进入我的域时重定向到该域,但没有 www。这个操作我之前在其他应用节点做过,没有问题,配置代码同上。
您的第一个服务器块没有证书定义。因此,您可能希望在该 server
块中复制 ssl_certificate
和 ssl_certificate_key
语句。
如果这些是唯一的 SSL server
块,并且因为您有两个域的通用证书文件,您可以将 ssl_certificate
和 ssl_certificate_key
语句移动到 http
级别并允许它们被两个 server
块继承。
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
server {
listen 443 ssl;
server_name www.example.com;
return 301 $scheme://example.com$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
...
}
有关更多信息,请参阅 this document。
(OP 确实要求在另一个答案的评论中查看我的 nodejs 代理代码。)
在我们进入 nodejs 代理之前,我们应该检查几件事:
证书
您的证书对有 www 和没有 www 都有效吗?
如果您的证书仅在没有 www 的情况下有效(或相反),那么代理(nginx 或我的或任何其他)设置都无济于事。因为 SSL/TLS 握手发生在重定向之前。在任何重定向发生之前,浏览器会立即投诉。最终用户在浏览器中看到证书错误。
我的两者都有效,您可以查看我的博客 https://johnsiu.com and https://www.johnsiu.com。重定向发生时没有任何证书投诉。
DNS
无论有无 www,您的 DNS 设置是否正确?它们应该指向相同的 IP 地址。
我想知道这是否与 ERR_SOCKET_NOT_CONNECTED 错误有关。
Nodejs 代理
托管在 ghithub:https://github.com/J-Siu/ghost-https-nodejs-proxy
刚刚微调我的nodejs代理,最新版本如下:
// HTTPS
const fs = require('fs');
const url = require('url');
const http = require('http');
const https = require('spdy'); // http2 support
const proxy = require('http-proxy').createProxyServer();
const compression = require('compression');
const ex = require('express')();
const fqdn = '<your domain name here>';
// Fill in your certificate files
const serverKey = '<KEY file>';
const serverCrt = '<CRT file>';
//const serverCa='<CA file>';
const httpsOptions = {
key: fs.readFileSync(serverKey),
cert: fs.readFileSync(serverCrt),
// ca: fs.readFileSync(serverCa),
ciphers: [
"ECDHE-RSA-AES256-SHA384",
"DHE-RSA-AES256-SHA384",
"ECDHE-RSA-AES256-SHA256",
"DHE-RSA-AES256-SHA256",
"ECDHE-RSA-AES128-SHA256",
"DHE-RSA-AES128-SHA256",
"HIGH",
"!aNULL",
"!eNULL",
"!EXPORT",
"!DES",
"!RC4",
"!MD5",
"!PSK",
"!SRP",
"!CAMELLIA"
].join(':'),
};
// Ghost Proxy configuration
proxy.on('proxyReq', function (proxyReq, req, res, options) {
// Ngix: proxy_set_header Host $http_host;
proxyReq.setHeader('Host', req.headers.host);
// Ngix: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxyReq.setHeader('X-Forwarded-For', req.connection.remoteAddress);
// Ngix: proxy_set_header X-Forwarded-Proto $scheme;
proxyReq.setHeader('X-Forwarded-Proto', 'https');
});
ex
.use(compression())
.use((req, res) => {
if (req.header.host == fqdn) {
proxy.web(req, res, { target: 'http://localhost:2368' });
} else {
res.writeHead(301, { 'location': 'https://' + fqdn + req.url });
res.end();
}
})
// HTTPS Server
https.createServer(httpsOptions, ex).listen(443, '0.0.0.0');
// HTTP Redirect to HTTPS
http.createServer(function (req, res) {
res.writeHead(301, { 'location': 'https://' + fqdn + req.url });
res.end();
}).listen(80, '0.0.0.0');
它能够:
- 将任何 http 流量重定向到 https(我在这里没有检查 url)
- 将任何 https 流量重定向到正确的 fqdn(任何与我的 fqdn 不匹配的请求都会被重定向)
ab
测试显示 ~16 请求/秒
我为我的单机单站点设置创建了这个,特别是为了在支持 HTTPS/HTTP2 的同时避免任何 apache/nginx 包。这可能无法完全满足您的需求,因为您正在做多个站点。
这是我的 Nginx 配置文件,我在其中使用 Ghost 为博客提供服务。该证书是使用 Let's Encrypt 生成的,并且适用于两个域。
缺点是在执行 301 重定向时出现,因为它不能正常工作,因为带有 www 的域告诉我证书无效,但是如果我尝试反向重定向,从 not www 到 www,它显示我同样的消息,但对于没有 www 的域:
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 80;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443;
server_name www.example.com;
ssl on;
ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
include snippets/ssl-params.conf;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://localhost:9002/;
proxy_redirect off;
}
}
目前,我正在使用Digital Ocean的一个droplet,我在其中配置了3个字段A,一个值为*的字段A指向虚拟机的IP,一个字段@也寻址IP服务器的地址,最后是一个值为 www 的字段 A,指向我的服务器的 IP。
周末的大部分时间我都在研究解决这种不便的方法,并设法找到了解决方法。
Let's Encrypt 和 Nginx 中都没有错误,都指的是我的错误配置;但是,我不能停止认为这是一个奇怪的事情,而且我的解决方案可以改进甚至不用多说,这是我的 Nginx 配置文件:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl on;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include snippets/ssl-params.conf;
if ($http_host = www.example.com) {
return 301 https://example.com$request_uri;
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://localhost:8080/;
proxy_ssl_session_reuse off;
proxy_set_header Host $http_host;
proxy_cache_bypass $http_upgrade;
proxy_redirect off;
}
}
为了避免拖拽错误,完全删除我的虚拟机并从头开始创建它,同时安装最新版本的Debian Nginx,即1.10.3,这使我能够使用http2,这也是一个改进。
一种解决方案是为 www 子域请求并安装第二个 SSL 证书。
步骤:
打开你的 nginx HTTP 配置文件:sudo nano /etc/nginx/sites-available/example.com.conf
。复制现有的服务器块。保持第一个块不变,这样它将继续为 example.com 工作。在第二个块中,在 server_name 行中包含 www:server_name www.example.com;
。保存并关闭文件。
打开您的 nginx SSL 配置文件:sudo nano /etc/nginx/sites-available/example.com-ssl.conf
。保持第一个块不变,这样它将继续为 example.com 工作。在第二个块中,在 server_name 行中包含 www,这将为您提供 server_name www.example.com;
。然后删除以 ssl_certificate
和 ssl_certificate_key
开头的行,因为我们不需要这些行,因为我们将为 www 子域请求新的 SSL 证书。保存并关闭文件。
为 www 子域申请 Let's Encrypt SSL 证书:
sudo /etc/letsencrypt/acme.sh --issue --home /etc/letsencrypt --domain www.example.com -- webroot /var/www/ghost/system/nginx-root --reloadcmd "nginx -s reload" --accountemail exampleEmailAddress@example.com
重新打开您的 nginx SSL 配置文件:sudo nano /etc/nginx/sites-available/example.com-ssl.conf
。 Return 到您在步骤 2 中设置的 www.example.com 服务器块并重新添加您的 ssl_certificate 行:ssl_certificate /etc/letsencrypt/www.example.com/fullchain.cer;
和 ssl_certificate_key /etc/letsencrypt/www.example.com/www.example.com.key;
。保存并关闭文件。
用sudo nginx -s reload
重新加载nginx,错误应该消失了。
我在 DigitalOcean 上有一个关于 Ghost 的博客。同样由Nginx服务,配置文件如下:
server {
listen 443 ssl;
server_name www.example.com;
return 301 $scheme://example.com$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include snippets/ssl-params.conf;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:2825;
}
}
证书是使用 Let's Encrypt 生成的,适用于带 www 和不带 www 的域。 在 Ghost config.js 文件中,URL 中写入了采用 SSL 的规则:https://example.com/ 问题是,当我进入我的博客时,我的域没有 wwww,正确登录并使用 SSL,但是当我尝试使用 https://www.example.com 登录时,我收到 SSL 证书身份验证错误。 我真的不明白这里可能有什么问题。 我需要在使用 www 进入我的域时重定向到该域,但没有 www。这个操作我之前在其他应用节点做过,没有问题,配置代码同上。
您的第一个服务器块没有证书定义。因此,您可能希望在该 server
块中复制 ssl_certificate
和 ssl_certificate_key
语句。
如果这些是唯一的 SSL server
块,并且因为您有两个域的通用证书文件,您可以将 ssl_certificate
和 ssl_certificate_key
语句移动到 http
级别并允许它们被两个 server
块继承。
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
server {
listen 443 ssl;
server_name www.example.com;
return 301 $scheme://example.com$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
...
}
有关更多信息,请参阅 this document。
(OP 确实要求在另一个答案的评论中查看我的 nodejs 代理代码。)
在我们进入 nodejs 代理之前,我们应该检查几件事:
证书
您的证书对有 www 和没有 www 都有效吗?
如果您的证书仅在没有 www 的情况下有效(或相反),那么代理(nginx 或我的或任何其他)设置都无济于事。因为 SSL/TLS 握手发生在重定向之前。在任何重定向发生之前,浏览器会立即投诉。最终用户在浏览器中看到证书错误。
我的两者都有效,您可以查看我的博客 https://johnsiu.com and https://www.johnsiu.com。重定向发生时没有任何证书投诉。
DNS
无论有无 www,您的 DNS 设置是否正确?它们应该指向相同的 IP 地址。
我想知道这是否与 ERR_SOCKET_NOT_CONNECTED 错误有关。
Nodejs 代理
托管在 ghithub:https://github.com/J-Siu/ghost-https-nodejs-proxy
刚刚微调我的nodejs代理,最新版本如下:
// HTTPS
const fs = require('fs');
const url = require('url');
const http = require('http');
const https = require('spdy'); // http2 support
const proxy = require('http-proxy').createProxyServer();
const compression = require('compression');
const ex = require('express')();
const fqdn = '<your domain name here>';
// Fill in your certificate files
const serverKey = '<KEY file>';
const serverCrt = '<CRT file>';
//const serverCa='<CA file>';
const httpsOptions = {
key: fs.readFileSync(serverKey),
cert: fs.readFileSync(serverCrt),
// ca: fs.readFileSync(serverCa),
ciphers: [
"ECDHE-RSA-AES256-SHA384",
"DHE-RSA-AES256-SHA384",
"ECDHE-RSA-AES256-SHA256",
"DHE-RSA-AES256-SHA256",
"ECDHE-RSA-AES128-SHA256",
"DHE-RSA-AES128-SHA256",
"HIGH",
"!aNULL",
"!eNULL",
"!EXPORT",
"!DES",
"!RC4",
"!MD5",
"!PSK",
"!SRP",
"!CAMELLIA"
].join(':'),
};
// Ghost Proxy configuration
proxy.on('proxyReq', function (proxyReq, req, res, options) {
// Ngix: proxy_set_header Host $http_host;
proxyReq.setHeader('Host', req.headers.host);
// Ngix: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxyReq.setHeader('X-Forwarded-For', req.connection.remoteAddress);
// Ngix: proxy_set_header X-Forwarded-Proto $scheme;
proxyReq.setHeader('X-Forwarded-Proto', 'https');
});
ex
.use(compression())
.use((req, res) => {
if (req.header.host == fqdn) {
proxy.web(req, res, { target: 'http://localhost:2368' });
} else {
res.writeHead(301, { 'location': 'https://' + fqdn + req.url });
res.end();
}
})
// HTTPS Server
https.createServer(httpsOptions, ex).listen(443, '0.0.0.0');
// HTTP Redirect to HTTPS
http.createServer(function (req, res) {
res.writeHead(301, { 'location': 'https://' + fqdn + req.url });
res.end();
}).listen(80, '0.0.0.0');
它能够:
- 将任何 http 流量重定向到 https(我在这里没有检查 url)
- 将任何 https 流量重定向到正确的 fqdn(任何与我的 fqdn 不匹配的请求都会被重定向)
ab
测试显示 ~16 请求/秒
我为我的单机单站点设置创建了这个,特别是为了在支持 HTTPS/HTTP2 的同时避免任何 apache/nginx 包。这可能无法完全满足您的需求,因为您正在做多个站点。
这是我的 Nginx 配置文件,我在其中使用 Ghost 为博客提供服务。该证书是使用 Let's Encrypt 生成的,并且适用于两个域。 缺点是在执行 301 重定向时出现,因为它不能正常工作,因为带有 www 的域告诉我证书无效,但是如果我尝试反向重定向,从 not www 到 www,它显示我同样的消息,但对于没有 www 的域:
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 80;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443;
server_name www.example.com;
ssl on;
ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
include snippets/ssl-params.conf;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://localhost:9002/;
proxy_redirect off;
}
}
目前,我正在使用Digital Ocean的一个droplet,我在其中配置了3个字段A,一个值为*的字段A指向虚拟机的IP,一个字段@也寻址IP服务器的地址,最后是一个值为 www 的字段 A,指向我的服务器的 IP。
周末的大部分时间我都在研究解决这种不便的方法,并设法找到了解决方法。 Let's Encrypt 和 Nginx 中都没有错误,都指的是我的错误配置;但是,我不能停止认为这是一个奇怪的事情,而且我的解决方案可以改进甚至不用多说,这是我的 Nginx 配置文件:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl on;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include snippets/ssl-params.conf;
if ($http_host = www.example.com) {
return 301 https://example.com$request_uri;
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://localhost:8080/;
proxy_ssl_session_reuse off;
proxy_set_header Host $http_host;
proxy_cache_bypass $http_upgrade;
proxy_redirect off;
}
}
为了避免拖拽错误,完全删除我的虚拟机并从头开始创建它,同时安装最新版本的Debian Nginx,即1.10.3,这使我能够使用http2,这也是一个改进。
一种解决方案是为 www 子域请求并安装第二个 SSL 证书。
步骤:
打开你的 nginx HTTP 配置文件:
sudo nano /etc/nginx/sites-available/example.com.conf
。复制现有的服务器块。保持第一个块不变,这样它将继续为 example.com 工作。在第二个块中,在 server_name 行中包含 www:server_name www.example.com;
。保存并关闭文件。打开您的 nginx SSL 配置文件:
sudo nano /etc/nginx/sites-available/example.com-ssl.conf
。保持第一个块不变,这样它将继续为 example.com 工作。在第二个块中,在 server_name 行中包含 www,这将为您提供server_name www.example.com;
。然后删除以ssl_certificate
和ssl_certificate_key
开头的行,因为我们不需要这些行,因为我们将为 www 子域请求新的 SSL 证书。保存并关闭文件。为 www 子域申请 Let's Encrypt SSL 证书:
sudo /etc/letsencrypt/acme.sh --issue --home /etc/letsencrypt --domain www.example.com -- webroot /var/www/ghost/system/nginx-root --reloadcmd "nginx -s reload" --accountemail exampleEmailAddress@example.com
重新打开您的 nginx SSL 配置文件:
sudo nano /etc/nginx/sites-available/example.com-ssl.conf
。 Return 到您在步骤 2 中设置的 www.example.com 服务器块并重新添加您的 ssl_certificate 行:ssl_certificate /etc/letsencrypt/www.example.com/fullchain.cer;
和ssl_certificate_key /etc/letsencrypt/www.example.com/www.example.com.key;
。保存并关闭文件。用
sudo nginx -s reload
重新加载nginx,错误应该消失了。