Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serve images directly through Nginx #743

Closed
1 of 2 tasks
kevinansfield opened this issue Jun 3, 2018 · 6 comments
Closed
1 of 2 tasks

Serve images directly through Nginx #743

kevinansfield opened this issue Jun 3, 2018 · 6 comments

Comments

@kevinansfield
Copy link
Contributor

This issue is a

  • Bug Report
  • Feature Request

Summary

Ghost's image handling is currently pretty basic. Even in "private mode" there is no node.js level gateway, all images are served as static assets. However, in the default Nginx config we still push all static image data through the node.js/express layer which adds overhead.

A default Ghost install has images files in a known directory so we can configure those files to be served directly via Nginx rather than passing it through the Ghost server.

Template changes

Example nginx config for serving image files directly:

    location ^~ /content/images/ {
        root /var/www/ghost;
        add_header Cache-Control "public, no-transform";
        add_header Vary "Accept-Encoding";
        expires max;
    }

In this case we would need the /var/www/ghost value to be interpolated. The validity of the no-transform header should also be discussed, details are here:

no-transform
No transformations or conversions should be made to the resource. The Content-Encoding, Content-Range, Content-Type headers must not be modified by a proxy. A non- transparent proxy might, for example, convert between image formats in order to save cache space or to reduce the amount of traffic on a slow link. The no-transform directive disallows this.

It may be that we want to allow CDNs or other proxies in front of a default Ghost install to perform additional compression.

Benchmarks

I performed a local benchmark on a DigitalOcean 1-click instance serving a 2.1MB image, SSL was used to replicate the typical stack:

siege -q -b -c25 -r50 https://ghost-test.lookingsideways.co.uk/content/images/2018/06/Cosmos01.jpg

As can be seen below, throughput doubled, response time is halved and there is much smaller deviation in overall transaction time.

Default config

Transactions:		        1250 hits
Availability:		      100.00 %
Elapsed time:		       43.17 secs
Data transferred:	     2486.66 MB
Response time:		        0.86 secs
Transaction rate:	       28.96 trans/sec
Throughput:		       57.60 MB/sec
Concurrency:		       24.86
Successful transactions:        1250
Failed transactions:	           0
Longest transaction:	        1.22
Shortest transaction:	        0.25

Nginx serving image

Transactions:		        1250 hits
Availability:		      100.00 %
Elapsed time:		       20.68 secs
Data transferred:	     2486.66 MB
Response time:		        0.41 secs
Transaction rate:	       60.44 trans/sec
Throughput:		      120.24 MB/sec
Concurrency:		       24.70
Successful transactions:        1250
Failed transactions:	           0
Longest transaction:	        0.47
Shortest transaction:	        0.07
@kirrg001
Copy link
Contributor

kirrg001 commented Jun 3, 2018

Serving images from the content folder can be configured differently. e.g. you can serve images via a storage adapter from aws s3. That means, Ghost has much more logic underneath of serving images. It's not necessarily a file on disk.

@kevinansfield
Copy link
Contributor Author

kevinansfield commented Jun 4, 2018

Agreed but that's not the default that's set up with Ghost-CLI. If publishers are setting up their own storage engines is it a stretch to also suggest they should configure their server config appropriately? Eg, with S3 storage if the adapter isn't returning URLs directly to the S3 assets then you'd want nginx configured to correctly cache the assets that are going through Ghost.

I guess the alternative would be creating a docs page for optimisations?

@ErisDS
Copy link
Member

ErisDS commented Jun 4, 2018

I would be very strongly in favour of this change as well as something similar for theme assets (would require some sort of symlink because the path is different, depending on the theme).

Adding these rules as optimisations to the docs would also be very preferable.

@jloh
Copy link
Member

jloh commented Jun 5, 2018

An alternative from a symlink would be using X-Accel header and internal locations. This complicates the Nginx setup a tiny bit but no more than adding an additional location block that would be added for this already. Doco can be found here.

This means Ghost wouldn't have to change any symlinks on the file system when changing themes or anything but does tie this feature to Nginx only (Apache also has support, but its under the X-Sendfile header.

Long term it means Ghost can handle all the authentication/permissions but pushes serving the heavy stuff (images, CSS, JS etc) over to Nginx once its done its checks freeing it up to do other things.

@kevinansfield
Copy link
Contributor Author

@jloh the problem with that approach is that it requires changes in Ghost itself and ties Ghost to Nginx. With the above approach we're only changing config in the default Ghost-CLI Nginx configuration, users of Ghost are still free to use a different web server, not use Ghost-CLI, or re-configure in a way they want.

If you want to use X-Accel-Redirect it would certainly be possible to create a custom storage engine that responds with the necessary headers for static content requests and then configure Nginx appropriately to respond to them.

@github-actions
Copy link

github-actions bot commented May 3, 2021

Our bot has automatically marked this issue as stale because there has not been any activity here in some time. The issue will be closed soon if there are no further updates, however we ask that you do not post comments to keep the issue open if you are not actively working on a PR. We keep the issue list minimal so we can keep focus on the most pressing issues. Closed issues can always be reopened if a new contributor is found. Thank you for understanding 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants