Hosting Two Ghost Blogs Under One Domain with Nginx
0x00 Requirements
Today, I want to host 2 different blogs on the same server under the same domain (xhyumiracle.com). It is extremely easy if you can endure accessing blogs with port number, like xhyumiracle.com:8000.
Apparently I can't, which means I want to access those two blogs by xhyumiracle.com and xhyumiracle.com/test. So basically, this is the core requirement.
This post includes the main process of how I solve the problem and what learned from it that.
- Ghost installation can be found on https://github.com/TryGhost/Ghost.
- If you have trouble accessing your new blog after installation, do check your firewall rules.
0x01 Nginx Overview (ref)
- installation:
$ sudo apt-get install nginx -y
- start/stop nginx:
$/etc/init.d/nginx start|stop|restart
- new configuration files can be added into
/etc/nginx/conf.d/
(CentOS) or/etc/nginx/sites-enabled
(Ubuntu). - if you find default sample Nginx configuration files under directories mentioned above, I would recommend deleting them.
rm /etc/nginx/sites-available/default
rm /etc/nginx/sites-enabled/default
rm /etc/nginx/conf.d/default
- you can create configuration files like
your-domain-name.conf
- then place the following into your file:
server {
listen 80;
server_name your-domain-name.com;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:2368;
}
}
- Now restart Nginx to make your changes take effect:
$ /etc/init.d/nginx restart
, nginx should forward all the requests from port 80 to port 2368.
0x02 Access the Second Blog by /test
- With the stuff mentioned above, it is easy to access a blog running on port 8000 by typing xhyumiracle.com (from port 80).
- But it's a little bit different when access another blog by typing xhyumiracle.com/test.
- First, we try to modify the location /:
server {
listen 80;
server_name xhyumiracle.com;
location /test {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:128;
}
}
- In this way, Nginx did transfer the request to the port 128, but the '/test' in the URI was also forwarded to ghost running on port 128 and ghost didn't know what does '/test' mean, so we have to remove it before handing it to ghost.
0x03 Nginx Rewrite
- Goal: To remove the '/test' from the URI.
- Syntax:
rewrite regex replacement [flag]
. flags: last|break|redirect|permanent - So just add one line before those proxy_set_header lines:
rewrite ^/test(.*) /$1 break
- In this way, ghost would be able to parse the URL forwarded by nginx correctly.
- But if you open your blog in the browser, you might see a plain text page without any css or image loaded.
- This is because the requests (including images, css, etc.) caused by index page are not start with /test, so nginx cannot forward them to port 128, so always lost them.
- Looks like we need to forward requests to 128 even without /test prefix.
0x04 $http_referer
- There are several ways to achieve that, you can modify the ghost core code to accept '/test' prefix, or prepend 'test' to every URL, or do things from the nginx side. Nginx way is better, so we're going to do it.
- Recalling that there is a Referrer field in the headers of requests to record the site who generated them originally, we can use it to tell out which requests belong to /test.
- In nginx, use
$http_referer
to get the referrer field. - So in location /, we need to place the following code:
location / {
if ($http_referer ~ ^http://xhyumiracle.com/test.*){
rewrite ^(.*) /test$1 redirect;
}
}
- In this way, we can identify requests from /test and redirect them to /test, then let location /test to forward them to port 128.
0x05 /test/ghost
- For now, nearly all the features are enabled under /test. Even we refresh the page in browser, it will return the right result.
- But the administrate page /test/ghost seems still 404 not found.
- I test it by return 403 as followed, it did return a 403 page when I type xhyumiracle.com/test/ghost, which means the problem is on ghost itself.
location /test {
return 403;
}
- F12 to open debug mode of chrome, I find there is a 301 redirect response of GET /ghost and a GET /ghost/ followed.
- In ghost code, I found that it will redirect
/ghost
to/ghost/
. In this way, new request doesn't carry a referrer header, so that's why our nginx failed again. - Knowing the reason, we can fix it by preventing the redirect happens.
- So we need to redirect it before it reaches ghost:
location = /test/ghost {
rewrite ghost$ ghost/ redirect;
}
- Restart nginx, you can see the /test/ghost worked.
- But, the last but in this article I promise, if you are newly set your blog and do not have an admin account yet, you might see another 404 error (lovely:).
- Let's fix it in a rude way this time.
0x06 /test/setup
- The error mentioned above is caused by /setup redirected from /ghost if there isn't an admin account. The /setup request, again, do not have referrer field in headers and failed our nginx rules.
- This time, I didn't come up with a nginx solution, so I dive into the ghost code trying to find out the redirect code.
- I did find it, it's in
your-ghost-path/core/server/middleware/redirect-to-setup.js
- Here is the redirect code snippet:
if (!exists.setup[0].status && !req.path.match(/\/setup\//)) {
return res.redirect(config.paths.subdir + '/ghost/setup/');
}
- So I just prepend the '/test' to the URL.
return res.redirect(config.paths.subdir + '/test/ghost/setup/');
- Restart your ghost blog, fixed.
0x07 Be Safe
- I know it would be dangerous and silly to publish nearly all the configurations. But I treat sharing knowledge as a much more important thing to others. So please do not hack this site, at least do not hack it with the knowledge from this post.
- Thank you for your visiting.