Full Code of shinsenter/docker-imgproxy for AI

main e4356a16e029 cached
12 files
54.1 KB
15.7k tokens
1 requests
Download .txt
Repository: shinsenter/docker-imgproxy
Branch: main
Commit: e4356a16e029
Files: 12
Total size: 54.1 KB

Directory structure:
gitextract_5zpmuxpl/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── .vscode/
│   ├── conf2doc.sh
│   └── settings.json
├── README.md
├── SECURITY.md
├── _config.yml
├── docker-compose.yml
├── imgproxy.conf
└── nginx/
    ├── Dockerfile
    ├── dhparam.pem
    └── nginx.conf

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
github: [shinsenter]
custom: [https://www.paypal.me/shinsenter]


================================================
FILE: .gitignore
================================================
.DS_Store
.vscode/*.txt
/cache
/certs
/logs


================================================
FILE: .vscode/conf2doc.sh
================================================
#!/bin/bash

BASE=$(dirname $0)
README=$BASE/../README.md
PATH="/opt/homebrew/opt/grep/libexec/gnubin:$PATH"

USAGE="$(
    grep -aPzo --color=never \
        '\n\n## (.|\n)*?\nmap (.|\n)*?\n}' \
        $BASE/../imgproxy.conf |
        tr -d '\0' |
        perl -pe 's/^\s*$//g' |
        perl -pe 's/^map/```nginx\nmap/g' |
        perl -pe 's/^}\s*/}\n```\n/g' |
        perl -pe 's/^#+ *(\*.*)$/\1<br\/>/g' |
        perl -pe 's/^#+ *(.*)$/\1/g' |
        perl -pe 's/^(\s+# +.*[^;\s])$/\n\1/g' |
        perl -pe 's/^/> /g' |
        perl -pe 's/^> +$/>/g' |
        perl -pe 's/```\s+/```\n\n\n/g'
)"

perl -i -p0e 's#> \*\*(.|\n)*```#@@@@@@#' $README

CONTENT="$(cat $README)"
echo "${CONTENT/@@@@@@/$USAGE}" >$README


================================================
FILE: .vscode/settings.json
================================================
{
    "editor.rulers": [80, 100],
    "files.trimTrailingWhitespace": true,
    "spellright.language": [],
    "spellright.documentTypes": [
        "latex",
        "plaintext"
    ]
}

================================================
FILE: README.md
================================================
# Ultra Image Server

![www/noimage_thumb.jpg](www/noimage_thumb.jpg)

🌐 A production grade on-the-fly image processing server setup using [imgproxy](https://imgproxy.net) and [Nginx caching](https://www.Nginx.com/products/Nginx/caching/).

## Table of contents
- [Why it's good](#why-its-good)
- [Getting started](#getting-started)
- [Examples](#examples)
- [Debugging](#debugging)
- [Configurations](#configurations)

---

![docker-imgproxy](www/project.jpg)

- **Author**: Mai Nhut Tan <shin@shin.company>
- **Copyright**: 2021-2024 SHIN Company <https://code.shin.company/docker-imgproxy#readme>


## Disclaimer

This project provides a solid foundation for building a custom image processing server. While it's designed for general use, you might need to tweak it further to perfectly match your specific needs.

I poured my heart into this project, hoping it serves as a valuable learning resource for anyone interested in exploring image processing configurations. If it doesn't fully address your current requirements, don't hesitate to keep searching for the perfect solution!


## Support my activities

If you find this project useful, consider donating via [PayPal](https://www.paypal.me/shinsenter) or open an issue on [Github](https://github.com/shinsenter/docker-imgproxy/discussions/new/choose). Your support helps keep this project maintained and improved for the community.


---


## Why it's good

Experience the ultimate in efficient image delivery with our cutting-edge solution:

- Lightning-fast on-the-fly thumbnail generation powered by [`imgproxy`](https://imgproxy.net).
- High availability and unparalleled performance thanks to Nginx content caching.
- Bid farewell to [URL signature](https://docs.imgproxy.net/usage/signing_url#configuring-url-signature) management hassles while maintaining top-notch security.
- Effortless serving of local files or remote server content.
- SEO-friendly URL crafting without exposing [complex `imgproxy` options](https://docs.imgproxy.net/usage/processing).
- Unmatched flexibility:
  - Customize image presets for any request.
  - Dynamic origin server selection.
  - Graceful fallback image handling for unavailable sources.
- Hassle-free SSL configuration with built-in HTTP/2 support for blazing-fast load times.
- Intelligent image format conversion to WebP (or AVIF) for browser compatibility.
- Seamless visual experience across devices.

Unleash the power of our cutting-edge solution and take your project to new heights with efficient image delivery.

---


## Getting started

### Pull this project from Github

Please run the below command to download the project to your local machine:

```shell
git clone https://github.com/shinsenter/docker-imgproxy.git docker-imgproxy
```

Or:

```shell
curl -skL https://github.com/shinsenter/docker-imgproxy/archive/refs/heads/main.zip -o docker-imgproxy.zip && unzip docker-imgproxy.zip
rm -f docker-imgproxy.zip
mv docker-imgproxy-main docker-imgproxy
```

### Prepare your files

Change your working directory to downloaded directory in the above step.

```shell
cd docker-imgproxy
```

Then put your image files to be served to the folder `www/`.

There are some sample files available (a sample image `cacti.jpg`, a watermark, and some fallback images).


### Start the server

```shell
cd docker-imgproxy
docker-compose up -d --build --remove-orphans --force-recreate
```

That's all! 😉

<small>Note: This setup requires [Docker Compose](https://docs.docker.com/compose/) installed. If you have already installed Docker Desktop or Docker Toolbox, then no need of installation for Docker Compose.</small>

If you like this setup, please [support my works](#support-my-activities) 😉.

---


## Examples

### Local images

Assuming that we want to generate various thumbnail images from the original file named `cacti.jpg`.

I already created some preset names, such as `_thumb` or `_w200`, and I add preset names to the original URL to get thumbnails from it.

> Image with no preset (it is resized to max-width=1600 as default).<br/>
> http://localhost/cacti.jpg


> The image with preset `_w200` applied (`200` is a dynamic number).<br/>
> http://localhost/_w200/cacti.jpg


> The image with preset `_blurry` applied.<br/>
> http://localhost/_blurry/cacti.jpg


> The image with preset `_small` applied.<br/>
> http://localhost/_small/cacti.jpg


> The image with preset `_medium` applied.<br/>
> http://localhost/_medium/cacti.jpg


> The image with preset `_thumb` applied.<br/>
> http://localhost/_thumb/cacti.jpg


> The image with preset `_square` applied.<br/>
> http://localhost/_square/cacti.jpg


> The image with preset `_masked` applied.<br/>
> http://localhost/_masked/cacti.jpg


> Or just to download the image (with filters applied).<br/>
> http://localhost/_download/cacti.jpg


See [my configurations](#advanced-settings) to know how it works.


### Remote images

With the same presets as above examples, we are going to serve an image [by NASA](https://mars.nasa.gov/system/downloadable_items/40368_PIA22228.jpg) using the alias `@nasa`, that will be added in these URLs.

<small>Note: the image source is from NASA, it may be unavailable in the future.</small>

> Image with no preset (it is resized to max-width=1600 as default).<br/>
> http://localhost/@nasa/40368_PIA22228.jpg


> The image with preset `_w200` applied (`200` is a dynamic number).<br/>
> http://localhost/@nasa/_w200/40368_PIA22228.jpg


> The image with preset `_blurry` applied.<br/>
> http://localhost/@nasa/_blurry/40368_PIA22228.jpg


> The image with preset `_small` applied.<br/>
> http://localhost/@nasa/_small/40368_PIA22228.jpg


> The image with preset `_medium` applied.<br/>
> http://localhost/@nasa/_medium/40368_PIA22228.jpg


> The image with preset `_thumb` applied.<br/>
> http://localhost/@nasa/_thumb/40368_PIA22228.jpg


> The image with preset `_square` applied.<br/>
> http://localhost/@nasa/_square/40368_PIA22228.jpg


> The image with preset `_masked` applied.<br/>
> http://localhost/@nasa/_masked/40368_PIA22228.jpg


> Or just to download the image (with filters applied).<br/>
> http://localhost/@nasa/_download/40368_PIA22228.jpg


#### Supported origin servers

This setup serve images from other public origin servers, as well as from Amazon S3 buckets, Google Cloud and Azure Blob.

You can learn how to serve files from private storage in the [configurations section](#serving-files-from-private-storage).


#### Base64 encoded URLs

The source URL can be encrypted with URL-safe Base64, prepended by the `/@base64/` prefix. So you can access the remote images like the below:

<small>Note: the image source is from NASA, it may be unavailable in the future.</small>

<p style="color:red">⚠️ Warning: Since this project has simplified the "URL signature" function of imgproxy, please be cautious with the use of Base64-encoded URLs. Malicious actors could exploit this to process images from any untrusted sources for unethical purposes.</p>

> Image with no preset (it is resized to max-width=1600 as default).<br/>
> http://localhost/@base64/aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjglNUZQSUEyMjIyOC5qcGc=


> The image with preset `_w200` applied (`200` is a dynamic number).<br/>
> http://localhost/@base64/_w200/aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjglNUZQSUEyMjIyOC5qcGc=


> The image with preset `_blurry` applied.<br/>
> http://localhost/@base64/_blurry/aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjglNUZQSUEyMjIyOC5qcGc=


> The image with preset `_small` applied.<br/>
> http://localhost/@base64/_small/aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjglNUZQSUEyMjIyOC5qcGc=


> The image with preset `_medium` applied.<br/>
> http://localhost/@base64/_medium/aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjglNUZQSUEyMjIyOC5qcGc=


> The image with preset `_thumb` applied.<br/>
> http://localhost/@base64/_thumb/aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjglNUZQSUEyMjIyOC5qcGc=


> The image with preset `_square` applied.<br/>
> http://localhost/@base64/_square/aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjglNUZQSUEyMjIyOC5qcGc=


> The image with preset `_masked` applied.<br/>
> http://localhost/@base64/_masked/aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjglNUZQSUEyMjIyOC5qcGc=


> Or just to download the image (with filters applied).<br/>
> http://localhost/@base64/_download/aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjglNUZQSUEyMjIyOC5qcGc=


### Customize resizing via query string

#### Image width and height

You can also parse arguments from the request's query string, such as `?width=300` for the image's width or `?height=200` for the image's height, or even both of demensions, to flexibly change some parameters for resizing.

In this setup example, I used the `width` and `height` arguments to override the existing presets.

> Image with specific demensions (1200x960).<br/>
> http://localhost/@nasa/40368_PIA22228.jpg?width=1200&height=960

> Image with `width` is set to 500px.<br/>
> http://localhost/@nasa/40368_PIA22228.jpg?width=500

> Image with `height` is set to 500px.<br/>
> http://localhost/@nasa/40368_PIA22228.jpg?height=500

> The image with preset `_medium` applied, but the query string will override the dimensions of the output image to 50x200px.<br/>
> http://localhost/@nasa/_medium/40368_PIA22228.jpg?width=50&height=200

#### Image quality

In addition, you can override the default quality defined by `IMGPROXY_FORMAT_QUALITY` in the `docker-compose.yml` file by passing a `quality` value (ranging from 1 to 100) in the query string of the request. For example, adding `?quality=100` will set the output image quality to 100% (the best quality).

> Image with `quality` is set.<br/>
> You can check the download size of the image using browser's Developer Tools.<br/>
> http://localhost/cacti.jpg?quality=1
>
> http://localhost/cacti.jpg?quality=10
>
> http://localhost/cacti.jpg?quality=50
>
> http://localhost/cacti.jpg?quality=80
>
> http://localhost/cacti.jpg?quality=100

> Image quality with a human readable `quality` value.<br/>
> You can check the download size of the image using browser's Developer Tools.<br/>
> http://localhost/cacti.jpg?quality=low
>
> http://localhost/cacti.jpg?quality=clear
>
> http://localhost/cacti.jpg?quality=high
>
> http://localhost/cacti.jpg?quality=highest

> You can combine the `quality` option with any above preset.<br/>
> http://localhost/_medium/cacti.jpg?quality=high
>
> http://localhost/_blurry/cacti.jpg?width=500&height=500&quality=1

See [my configurations](#advanced-settings) to know how it works.

---


## Debugging


### Debugging rewrite rule:

When you make a request with the query component `debug=1`, you will see an `X-Debug` header contains its internal `imgproxy`'s options.

Example 1 (local file with preset `_small`):
```
curl -Isk 'http://localhost/_small/cacti.jpg?debug=1'

HTTP/1.1 200 OK
Server: nginx
Content-Type: image/webp
X-Debug: /unsafe/size:320:320:0:0/sharpen:0.3/preset:logo/dpr:1/plain/local:///cacti.jpg
```

Example 2 (remote file with preset `_w640`):
```
curl -Isk 'http://localhost/@nasa/_w640/40368_PIA22228.jpg?debug=1'

HTTP/1.1 200 OK
Server: nginx
Content-Type: image/webp
X-Debug: /unsafe/size:640:0:0:0/preset:logo/dpr:1/plain/https://mars.nasa.gov/system/downloadable_items/40368_PIA22228.jpg
```


### Debugging fallback image:

When you requested a file which is not available, a fallback image will be served.
There will be a 404 response with a header called `X-Fallback`, which contains the fallback image's path.

```
curl -Isk 'http://localhost/i-am-a-teacup.jpg'

HTTP/1.1 404 Not Found
Server: nginx
Content-Type: image/jpeg
X-Fallback: /noimage.jpg
```


### Other helper URL parameters:

We can use these URL query components to modify some requests.

> `skip=1`<br/>
> Skip imgproxy processing for current request. The original file will be served.<br/>
> E.g. http://localhost/_small/cacti.jpg?skip=1 <br/>
> E.g. http://localhost/@nasa/_small/40368_PIA22228.jpg?skip=1

> `nocache=1`<br/>
> Disable Nginx caching for current request. The response will not be saved to a cache.<br/>
> E.g. http://localhost/cacti.jpg?nocache=1 <br/>
> E.g. http://localhost/@nasa/40368_PIA22228.jpg?nocache=1

> `bypass=1`<br/>
> By pass Nginx caching for current request. The response will not be taken from a cache.<br/>
> E.g. http://localhost/_small/cacti.jpg?bypass=1 <br/>
> E.g. http://localhost/@nasa/_small/40368_PIA22228.jpg?bypass=1

If you like this setup, please [support my works](#support-my-activities) 😉.

---


## Configurations


### Enabling SSL (and HTTP/2)

Create a folder `certs/` in the same place with the `docker-compose.yml` file, then rename and put your SSL certificates `server.crt` and `server.key` to that `certs/` folder.

Open the file at [`nginx/nginx.conf`](nginx/nginx.conf#L120~L124) and uncomment 4 lines right after the `# SSL` line, like this:

```nginx
# SSL
listen              443 ssl http2 reuseport;
listen              [::]:443 ssl http2 reuseport;
ssl_certificate     /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
```

Then run the command in the [Start the server](#start-the-server) section to recreate and restart the service.


### Serving files from private storage

Please uncomment settings in `docker-compose.yml` file to enable serving files from [Amazon S3 buckets](docker-compose.yml#L177~L182), [Google Cloud](docker-compose.yml#L184~L187) or [Azure Blob](docker-compose.yml#L189~L194), etc. Then run the command in the [Start the server](#start-the-server) section to recreate and restart the service.

You can find more details on [imgproxy documentation](https://docs.imgproxy.net/configuration/options?#image-sources).


### Flushing cache files

Just remove the folder `cache/`.

```shell
rm -rf cache/
```

Then run the command in the [Start the server](#start-the-server) section to restart the service.


### Advanced settings

All settings for handling image URLs are written in the [`imgproxy.conf`](imgproxy.conf#L70~L295) file using [Nginx's map directives](https://Nginx.org/en/docs/http/ngx_http_map_module.html#directives).

I keep all configurations in very simple variables. You can also make your own version from this base.


> **`$use_imgproxy`**<br/>
> This flag indicates that the request will be proceeded by `imgproxy`.
> ```nginx
> map $uri_omitted_origin_preset $use_imgproxy
> {
>     default 0;
>
>     # Add any rules that you want to skip image processing.
>     #> E.g. this line excludes files under "hq-cactus" folder.
>     ~^/hq-cactus/ 0;
>
>     # File URL is base64-encoded
>     #> Warning: Since this project has simplified the "URL signature" function of imgproxy,
>     #> please be cautious with the use of Base64-encoded URLs.
>     #> Malicious actors could exploit this to process images from any untrusted sources for unethical purposes.
>     #> Comment out these two lines to disable Base64-encoded URLs.
>     ~^/@base64/         1;
>     ~[-A-Za-z0-9+/]*=*$ 1;
>
>     # Else, process all image files with these file extensions
>     ~*\.(jpe?g|png|gif|tiff?|bmp)$  1;
> }
> ```


> **`$origin_server`**<br/>
> Define origin base URL from the request.
> This setup assumes that an origin server starts with an `@` symbol (such as `@nasa`, `@pinterest`, etc.).
> You can also add your own origin servers using [regular expressions](https://www.nginx.com/blog/regular-expression-tester-nginx/).
> ```nginx
> map $uri $origin_server
> {
>     default         'local://';
>
>     # Put your rewrite rules for origin servers from here.
>     #> E.g.
>     ~^/@mybucket/   's3://my-bucket';
>     ~^/@myhost/     'http://myhost.com';
>     ~^/@nasa/       'https://mars.nasa.gov/system/downloadable_items';
>     ~^/@pinterest/  'https://i.pinimg.com/originals';
>
>     # Source URL can be encoded with URL-safe Base64 (please be cautious!)
>     #> See: https://docs.imgproxy.net/usage/processing#source-url
>     #> Warning: Since this project has simplified the "URL signature" function of imgproxy,
>     #> please be cautious with the use of Base64-encoded URLs.
>     #> Malicious actors could exploit this to process images from any untrusted sources for unethical purposes.
>     #> Comment out the below line to disable Base64-encoded URLs.
>     ~^/@base64/     ''; # no origin server
> }
> ```


> **`$origin_uri`**<br/>
> Parse real origin URI of the file.
> This setup just omits origin server and preset name in the URI if they exist,
> but you can also rewrite requested URI using [regular expressions](https://www.nginx.com/blog/regular-expression-tester-nginx/).
> ```nginx
> map $uri_omitted_origin_preset $origin_uri
> {
>     default '$uri_omitted_origin_preset';
>
>     # Put your rewrite rules for origin URI from here.
>     #> E.g. this line rewrites cactus.jpg to the real path cacti.jpg.
>     ~*^/cactus\.jpe?g$  '/cacti.jpg';
> }
> ```


> **`$preset_name`**<br/>
> Parse preset name from requested URI.
> This setup assumes that preset name starts with an underscore (`_`) symbol (such as `_thumb` or `_w200`).
> You can define your own presets using [regular expressions](https://www.nginx.com/blog/regular-expression-tester-nginx/).
> ```nginx
> map $uri_omitted_origin $preset_name
> {
>     default '';
>
>     # You can define dynamic presets,
>     #> but beware that dynamic presets are able to cause a denial-of-service attack
>     #> by allowing an attacker to request multiple different image resizes.
>     #> E.g. a dynamic preset with a variable $width.
>     # ~^/_w(?<parsed_width>[0-9_-]+)/  'max_w:${parsed_width}';
>
>     # This is a better version for above dynamic preset.
>     #> It allows only certain image sizes,
>     #> and fallbacks other undefined image sizes to max_w:200
>     ~^/_w(?<parsed_width>(200|640|800|1200|1600))/  'max_w:${parsed_width}';
>     ~^/_w(?<parsed_width>([0-9_-]+))/               'max_w:200';
>
>     # Get static preset name from the URI
>     ~^/_(?<parsed_name>[a-z0-9_-]+)/ '$parsed_name';
> }
> ```


> **`$imgproxy_preset`**<br/>
> Define `imgproxy` options for each preset name.
> You can view more details by following [their documentation](https://docs.imgproxy.net/usage/processing?#processing-options).
> ```nginx
> map $preset_name $imgproxy_preset
> {
>     default 'size:1600:0:0:0/preset:logo'; # preset:logo adds watermark to the image
>
>     # Dynamic preset
>     ~^max_w:(?<width>[0-9]+)$ 'size:${width}:0:0:0/preset:logo';
>
>     # Static presets
>     blurry   'size:320:320:1:0/blur:10/quality:50';
>     small    'size:320:320:0:0/sharpen:0.3/preset:logo';
>     medium   'size:640:640:0:0/preset:logo';
>     thumb    'size:160:160:1:1/bg:ffffff/resizing_type:fill/sharpen:0.3';
>     square   'size:500:500:0:1/bg:ffffff/resizing_type:fill/preset:logo';
>     masked   'size:500:0:0:1/bg:ffffff/resizing_type:fill/preset:repeated_logo';
>     download 'size:1600:0:0:0/preset:logo/return_attachment:1';
> }
> ```


> **`$imgproxy_preset_query` (overriding presets with query string)**<br/>
> Override the `$imgproxy_preset` whenever `width` or `height` provided.
> But beware that dynamic image sizes are able to cause a denial-of-service attack by allowing an attacker to request multiple different image resizes.
> ```nginx
> map "$arg_width:$arg_height" $imgproxy_preset_query
> {
>     default '';
>
>     # only width
>     ~^(?<width>[0-9]+):$ '/size:${width}:0:1:0/bg:ffffff/resizing_type:fill';
>
>     # only height
>     ~^:(?<height>[0-9]+)$ '/size:0:${height}:1:0/bg:ffffff/resizing_type:fill';
>
>     # both width and height
>     ~^(?<width>[0-9]+):(?<height>[0-9]+)$ '/size:${width}:${height}:1:0/bg:ffffff/resizing_type:fill';
> }
> ```


> **`$imgproxy_quality` (overriding photo quality with query string)**<br/>
> Control photo quality with query string. You can also add your custom settings.
> ```nginx
> map $arg_quality $imgproxy_quality
> {
>     default '';
>
>     # if the given value is between 1 and 100, override the quality
>     ~^(?<quality>[1-9][0-9]?|100)$ '/q:${quality}';
>
>     # or receive some readable quality values
>     low         '/q:30';
>     clear       '/q:50';
>     high        '/q:80';
>     highest     '/q:100';
> }
> ```


> **`$imgproxy_dpr` (controlling photo dimensions, aka Device Pixel Ratio)**<br/>
> This will multiplying the image dimensions according to this factor for HiDPI (Retina) devices.
> ```nginx
> map $http_user_agent@$http_dpr $imgproxy_dpr
> {
>     default '/dpr:1';
>
>     # parse from DPR header
>     ~@(?<dpr>[1-4])     '/dpr:${dpr}';
>
>     # Put your rewrite rules for DPR settings from here.
>     #> E.g. these lines will set custom DPR for smartphones.
>     # ~iPhone|iPad|Mac    '/dpr:3';
>     # ~Android            '/dpr:2';
> }
> ```


> **`$imgproxy_options`**<br/>
> Generate final URL for `imgproxy` following [their documentation](https://docs.imgproxy.net/usage/processing).
> When URL query `?skip=1` is set, use another rule to skip `imgproxy` processing.
> ```nginx
> map $arg_skip $imgproxy_options
> {
>     default '/unsafe/${imgproxy_preset}${imgproxy_preset_query}${imgproxy_quality}${imgproxy_dpr}${imgproxy_type}/${origin_server}${origin_uri}${imgproxy_extension}';
>     ~.+     '/unsafe${imgproxy_type}/${origin_server}${origin_uri}';
> }
> ```


> **`$fallback_uri`**<br/>
> Define fallback file to serve when the requested file is unavailable.
> E.g. `/noimage.jpg` or `/noimage_thumb.jpg`, which is stored in the folder `www/`.
> ```nginx
> map $preset_name $fallback_uri
> {
>     default '/noimage.jpg';
>     thumb   '/noimage_thumb.jpg';
>     # small   '/noimage_small.jpg';
>     # medium  '/noimage_medium.jpg';
>     # square  '/noimage_square.jpg';
> }
> ```


I think you can even make a better version than mine 😊.


* * *

If you like this project, please [support my works](#support-my-activities) 😉.

From Vietnam 🇻🇳 with love.


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Reporting a Vulnerability

Use this section to tell people how to report a vulnerability.

Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.


================================================
FILE: _config.yml
================================================
theme: jekyll-theme-hacker

================================================
FILE: docker-compose.yml
================================================
################################################################################
# Ultra Image Server
# A production grade image processing server setup powered by imgproxy and nginx
#
# Author: Mai Nhut Tan <shin@shin.company>
# Copyright: 2021-2024 SHIN Company https://code.shin.company/
# URL: https://shinsenter.github.io/docker-imgproxy/
################################################################################

networks:
################################################################################
  default:
    driver: bridge


services:
################################################################################
  nginx:
    restart: unless-stopped
    build: ./nginx
    container_name: imgproxy_gateway
    volumes:
      - ./imgproxy.conf:/etc/nginx/nginx.d/imgproxy.conf
      - ./www:/home:cached
      - ./certs:/etc/nginx/certs:cached
      - ./cache:/var/cache/nginx/imgproxy:delegated
      - ./logs:/var/log/nginx:delegated
    ports:
      - 80:80
      - 443:443/tcp
      - 443:443/udp
    links:
      - imgproxy:imgproxy
    environment:
      NGINX_ENTRYPOINT_QUIET_LOGS: 1

################################################################################
  imgproxy:
    restart: unless-stopped
    image: darthsim/imgproxy:${IMGPROXY_TAG:-latest}
    container_name: imgproxy_app
    healthcheck:
      test: [ "CMD", "imgproxy", "health" ]
      timeout: "10s"
      interval: "10s"
      retries: 3
    volumes:
      - ./www:/home:cached
    environment:
      ### Server
      ### See: https://docs.imgproxy.net/configuration/options#server
      IMGPROXY_NETWORK: "tcp"
      IMGPROXY_BIND: ":8080"

      ### Image sources
      ### See: https://docs.imgproxy.net/configuration/options#image-sources
      IMGPROXY_LOCAL_FILESYSTEM_ROOT: /home

      ### Debug
      ### See: https://docs.imgproxy.net/configuration/options#log
      IMGPROXY_LOG_LEVEL: "warn"
      IMGPROXY_LOG_FORMAT: "pretty"
      IMGPROXY_DEVELOPMENT_ERRORS_MODE: false
      IMGPROXY_ENABLE_DEBUG_HEADERS: false
      IMGPROXY_REPORT_DOWNLOADING_ERRORS: false

      ### Client
      IMGPROXY_READ_REQUEST_TIMEOUT: 10
      IMGPROXY_TIMEOUT: 10
      IMGPROXY_KEEP_ALIVE_TIMEOUT: 10
      IMGPROXY_CLIENT_KEEP_ALIVE_TIMEOUT: 90
      IMGPROXY_DOWNLOAD_TIMEOUT: 10
      IMGPROXY_BASE_URL: ""
      IMGPROXY_CACHE_CONTROL_PASSTHROUGH: false
      IMGPROXY_ETAG_BUSTER: ""
      IMGPROXY_HEALTH_CHECK_PATH: ""
      IMGPROXY_IGNORE_SSL_VERIFICATION: true
      IMGPROXY_MAX_CLIENTS: 2048
      IMGPROXY_MAX_REDIRECTS: 10
      IMGPROXY_PATH_PREFIX: ""
      IMGPROXY_REQUESTS_QUEUE_SIZE: 0
      IMGPROXY_SET_CANONICAL_HEADER: true
      IMGPROXY_SO_REUSEPORT: true
      IMGPROXY_TTL: 31536000
      IMGPROXY_USE_ETAG: false
      IMGPROXY_USE_LAST_MODIFIED: false

      ### Memory usage tweaks
      ### See: https://docs.imgproxy.net/configuration/options#memory-usage-tweaks
      IMGPROXY_FREE_MEMORY_INTERVAL: 10
      IMGPROXY_DOWNLOAD_BUFFER_SIZE: 0
      IMGPROXY_BUFFER_POOL_CALIBRATION_THRESHOLD: 1024

      ### Security
      ### See: https://docs.imgproxy.net/configuration/options#security
      IMGPROXY_SALT: ""
      IMGPROXY_KEY: ""
      IMGPROXY_SECRET: ""
      IMGPROXY_SOURCE_URL_ENCRYPTION_KEY: ""
      IMGPROXY_SIGNATURE_SIZE: 32
      IMGPROXY_ALLOW_ORIGIN: ""
      IMGPROXY_ALLOWED_SOURCES: ""
      IMGPROXY_ALLOW_LINK_LOCAL_SOURCE_ADDRESSES: false
      IMGPROXY_ALLOW_LOOPBACK_SOURCE_ADDRESSES: false
      IMGPROXY_ALLOW_PRIVATE_SOURCE_ADDRESSES: true
      IMGPROXY_ALLOW_SECURITY_OPTIONS: false
      IMGPROXY_MAX_ANIMATION_FRAME_RESOLUTION: 0
      IMGPROXY_MAX_ANIMATION_FRAMES: 64
      IMGPROXY_MAX_SRC_FILE_SIZE: 0
      IMGPROXY_MAX_SRC_RESOLUTION: 50
      IMGPROXY_MAX_SVG_CHECK_BYTES: 32768 # 32KB

      ### Cookies
      ### See: https://docs.imgproxy.net/configuration/options#cookies
      IMGPROXY_COOKIE_PASSTHROUGH: false
      IMGPROXY_COOKIE_BASE_URL: ""

      ### Compression
      ### See: https://docs.imgproxy.net/configuration/options#compression
      IMGPROXY_QUALITY: 80
      IMGPROXY_FORMAT_QUALITY: jpeg=80,webp=70,avif=50
      IMGPROXY_JPEG_PROGRESSIVE: false
      IMGPROXY_PNG_INTERLACED: false
      IMGPROXY_PNG_QUANTIZATION_COLORS: 256
      IMGPROXY_PNG_QUANTIZE: false
      IMGPROXY_AVIF_SPEED: 9
      IMGPROXY_AUTO_AVIF: false
      IMGPROXY_ENFORCE_AVIF: false
      IMGPROXY_AUTO_WEBP: false
      IMGPROXY_ENFORCE_WEBP: false

      ### Preferred formats
      ### See: https://docs.imgproxy.net/configuration/options#preferred-formats
      IMGPROXY_PREFERRED_FORMATS: jpeg,png,gif

      ### Skip processing
      ### https://docs.imgproxy.net/configuration/options#skip-processing
      IMGPROXY_SKIP_PROCESSING_FORMATS: "svg,webp,avif"

      ### Client Hints support
      ### See: https://docs.imgproxy.net/configuration/options#client-hints-support
      IMGPROXY_ENABLE_CLIENT_HINTS: true

      ### Watermark
      ### See: https://docs.imgproxy.net/configuration/options#watermark
      IMGPROXY_WATERMARK_OPACITY: 1
      IMGPROXY_WATERMARK_PATH: "/home/watermark.jpg"
      IMGPROXY_WATERMARK_DATA: ""
      IMGPROXY_WATERMARK_URL: ""

      ### Fallback image
      ### See: https://docs.imgproxy.net/configuration/options#fallback-image
      IMGPROXY_FALLBACK_IMAGE_TTL: 0
      IMGPROXY_FALLBACK_IMAGE_HTTP_CODE: 404
      IMGPROXY_FALLBACK_IMAGE_PATH: "/home/noimage.jpg"
      IMGPROXY_FALLBACK_IMAGE_DATA: ""
      IMGPROXY_FALLBACK_IMAGE_URL: ""

      ### Presets
      ### See: https://docs.imgproxy.net/configuration/options#presets
      IMGPROXY_PRESETS: "default=resizing_type:fit/gravity:sm,logo=watermark:0.8:soea:20:20:0.15,repeated_logo=watermark:0.25:re:0:0:0.2"
      IMGPROXY_ONLY_PRESETS: false

      ### Miscellaneous
      ### See: https://docs.imgproxy.net/configuration/options#miscellaneous
      IMGPROXY_AUTO_ROTATE: true
      IMGPROXY_DISABLE_SHRINK_ON_LOAD: false
      IMGPROXY_ENFORCE_THUMBNAIL: false
      IMGPROXY_KEEP_COPYRIGHT: false
      IMGPROXY_RETURN_ATTACHMENT: false
      IMGPROXY_SANITIZE_SVG: true
      IMGPROXY_STRIP_COLOR_PROFILE: true
      IMGPROXY_STRIP_METADATA: true
      IMGPROXY_SVG_FIX_UNSUPPORTED: false
      IMGPROXY_USE_LINEAR_COLORSPACE: false

      # ### Amazon S3
      # ### See: https://docs.imgproxy.net/configuration/options#amazon-s3
      # IMGPROXY_USE_S3: false
      # IMGPROXY_S3_REGION: ""
      # IMGPROXY_S3_ENDPOINT: ""
      # IMGPROXY_S3_MULTI_REGION: false

      # ### Google Cloud Storage
      # ### See: https://docs.imgproxy.net/configuration/options#google-cloud-storage
      # IMGPROXY_USE_GCS: false
      # IMGPROXY_GCS_KEY: ""

      # ### Azure Blob Storage
      # ### See: https://docs.imgproxy.net/configuration/options#azure-blob-storage
      # IMGPROXY_USE_ABS: false
      # IMGPROXY_ABS_NAME: ""
      # IMGPROXY_ABS_KEY: ""
      # IMGPROXY_ABS_ENDPOINT: ""

      # ### OpenStack Object Storage
      # ### See: https://docs.imgproxy.net/configuration/options#serving-files-from-openstack-object-storage-swift
      # IMGPROXY_USE_SWIFT: false
      # IMGPROXY_SWIFT_USERNAME: ""
      # IMGPROXY_SWIFT_API_KEY: ""
      # IMGPROXY_SWIFT_AUTH_URL: ""
      # IMGPROXY_SWIFT_AUTH_VERSION: 0
      # IMGPROXY_SWIFT_TENANT: ""
      # IMGPROXY_SWIFT_DOMAIN: ""
      # IMGRPOXY_SWIFT_CONNECT_TIMEOUT_SECONDS: 10
      # IMGRPOXY_SWIFT_TIMEOUT_SECONDS: 60

      # ### New Relic
      # ### See: https://docs.imgproxy.net/configuration/options#new-relic
      # IMGPROXY_NEW_RELIC_APP_NAME: ""
      # IMGPROXY_NEW_RELIC_KEY: ""

      # ### Prometheus
      # ### See: https://docs.imgproxy.net/configuration/options#prometheus
      # IMGPROXY_PROMETHEUS_BIND: ""
      # IMGPROXY_PROMETHEUS_NAMESPACE: ""

      # ### OpenTelemetry
      # ### See: https://docs.imgproxy.net/configuration/options#opentelemetry
      # IMGPROXY_OPEN_TELEMETRY_ENABLE_METRICS: false
      # IMGPROXY_OPEN_TELEMETRY_SERVER_CERT: ""
      # IMGPROXY_OPEN_TELEMETRY_CLIENT_CERT: ""
      # IMGPROXY_OPEN_TELEMETRY_CLIENT_KEY: ""
      # IMGPROXY_OPEN_TELEMETRY_TRACE_ID_GENERATOR: "xray"

      # ### Amazon CloudWatch metrics
      # ### See: https://docs.imgproxy.net/configuration/options#amazon-cloudwatch-metrics
      # IMGPROXY_CLOUD_WATCH_SERVICE_NAME: ""
      # IMGPROXY_CLOUD_WATCH_NAMESPACE: "imgproxy"
      # IMGPROXY_CLOUD_WATCH_REGION: ""

      # ### Bugsnag
      # ### See: https://docs.imgproxy.net/configuration/options#bugsnag
      # IMGPROXY_BUGSNAG_KEY: ""
      # IMGPROXY_BUGSNAG_STAGE: "production"

      # ### Honeybadger
      # ### See: https://docs.imgproxy.net/configuration/options#honeybadger
      # IMGPROXY_HONEYBADGER_KEY: ""
      # IMGPROXY_HONEYBADGER_ENV: "production"

      # ### Sentry
      # ### See: https://docs.imgproxy.net/configuration/options#sentry
      # IMGPROXY_SENTRY_DSN: ""
      # IMGPROXY_SENTRY_ENVIRONMENT: "production"

      # ### Airbrake
      # ### See: https://docs.imgproxy.net/configuration/options#airbrake
      # IMGPROXY_AIRBRAKE_PROJECT_ID: 0
      # IMGPROXY_AIRBRAKE_PROJECT_KEY: ""
      # IMGPROXY_AIRBRAKE_ENVIRONMENT: "production"

================================================
FILE: imgproxy.conf
================================================
################################################################################
# Ultra Image Server
# A production grade image processing server setup powered by `imgproxy` and nginx
#
# Author: Mai Nhut Tan <shin@shin.company>
# Copyright: 2021-2024 SHIN Company https://code.shin.company/
# URL: https://shinsenter.github.io/docker-imgproxy/
################################################################################


# ======================================================================== #
# Block access from some bad IPs
# See: http://nginx.org/en/docs/http/ngx_http_access_module.html#deny
# Add your bad client IPs here. E.g.
# deny 134.119.219.93;



# ======================================================================== #
# This upstream links to `imgproxy` container outside.
# Do not change this upstream
# unless you are going to change `imgproxy`'s container name.

upstream upstream_imgproxy  {
    server    imgproxy:8080;
    keepalive 16;
}



# ======================================================================== #
# Cache zone
# See: https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_path
#
# Let's assume average file size is ~20kb
# max_size:  1mb of keys_zone can store 7,000 keys * 20kb = ~140mb
#            4mb of keys_zone can store ~28,000 keys = ~560mb
#            32mb of keys_zone can store ~224,000 keys = ~4.8gb
#

proxy_cache_path
    /var/cache/nginx/imgproxy levels=1:2 use_temp_path=off
    keys_zone=IMAGE_CACHE:32m max_size=5G min_free=32m inactive=7d;



# ======================================================================== #
# Temporary variables

##! **`$uri_omitted_origin`**
## Get the URI after omitting origin server.
## This setup assumes that an origin server starts with an `@` symbol.
map $file_uri $uri_omitted_origin
{
    default '$file_uri';
    ~^(/@[^/]+)?(?<parsed_path>/.+)$        '$parsed_path';
}

##! **`$uri_omitted_origin_preset`**
## Get the URI after omitting origin server and preset name.
## This setup assumes that preset name starts with an underscore (`_`) symbol.
map $uri_omitted_origin $uri_omitted_origin_preset
{
    default '$uri_omitted_origin';
    ~*^(/_[a-z0-9_-]+)?(?<parsed_path>/.+)$ '$parsed_path';
}



# ======================================================================== #
# Variables for `imgproxy`

## **`$use_imgproxy`**
## This flag indicates that the request will be proceeded by `imgproxy`.
map $uri_omitted_origin_preset $use_imgproxy
{
    default 0;

    # Add any rules that you want to skip image processing.
    #> E.g. this line excludes files under "hq-cactus" folder.
    ~^/hq-cactus/ 0;

    # File URL is base64-encoded
    #> Warning: Since this project has simplified the "URL signature" function of imgproxy,
    #> please be cautious with the use of Base64-encoded URLs.
    #> Malicious actors could exploit this to process images from any untrusted sources for unethical purposes.
    #> Comment out these two lines to disable Base64-encoded URLs.
    ~^/@base64/         1;
    ~[-A-Za-z0-9+/]*=*$ 1;

    # Else, process all image files with these file extensions
    ~*\.(jpe?g|png|gif|tiff?|bmp)$  1;
}

##! **`$imgproxy_cache`**
## Default cache zone for `imgproxy`.
map $use_imgproxy $imgproxy_cache
{
    default off;
    1       IMAGE_CACHE;
}

## **`$origin_server`**
## Define origin base URL from the request.
## This setup assumes that an origin server starts with an `@` symbol (such as `@nasa`, `@pinterest`, etc.).
## You can also add your own origin servers using [regular expressions](https://www.nginx.com/blog/regular-expression-tester-nginx/).
map $uri $origin_server
{
    default         'local://';

    # Put your rewrite rules for origin servers from here.
    #> E.g.
    ~^/@mybucket/   's3://my-bucket';
    ~^/@myhost/     'http://myhost.com';
    ~^/@nasa/       'https://mars.nasa.gov/system/downloadable_items';
    ~^/@pinterest/  'https://i.pinimg.com/originals';

    # Source URL can be encoded with URL-safe Base64 (please be cautious!)
    #> See: https://docs.imgproxy.net/usage/processing#source-url
    #> Warning: Since this project has simplified the "URL signature" function of imgproxy,
    #> please be cautious with the use of Base64-encoded URLs.
    #> Malicious actors could exploit this to process images from any untrusted sources for unethical purposes.
    #> Comment out the below line to disable Base64-encoded URLs.
    ~^/@base64/     ''; # no origin server
}

## **`$origin_uri`**
## Parse real origin URI of the file.
## This setup just omits origin server and preset name in the URI if they exist,
## but you can also rewrite requested URI using [regular expressions](https://www.nginx.com/blog/regular-expression-tester-nginx/).
map $uri_omitted_origin_preset $origin_uri
{
    default '$uri_omitted_origin_preset';

    # Put your rewrite rules for origin URI from here.
    #> E.g. this line rewrites cactus.jpg to the real path cacti.jpg.
    ~*^/cactus\.jpe?g$  '/cacti.jpg';
}

## **`$preset_name`**
## Parse preset name from requested URI.
## This setup assumes that preset name starts with an underscore (`_`) symbol (such as `_thumb` or `_w200`).
## You can define your own presets using [regular expressions](https://www.nginx.com/blog/regular-expression-tester-nginx/).
map $uri_omitted_origin $preset_name
{
    default '';

    # You can define dynamic presets,
    #> but beware that dynamic presets are able to cause a denial-of-service attack
    #> by allowing an attacker to request multiple different image resizes.
    #> E.g. a dynamic preset with a variable $width.
    # ~^/_w(?<parsed_width>[0-9_-]+)/  'max_w:${parsed_width}';

    # This is a better version for above dynamic preset.
    #> It allows only certain image sizes,
    #> and fallbacks other undefined image sizes to max_w:200
    ~^/_w(?<parsed_width>(200|640|800|1200|1600))/  'max_w:${parsed_width}';
    ~^/_w(?<parsed_width>([0-9_-]+))/               'max_w:200';

    # Get static preset name from the URI
    ~^/_(?<parsed_name>[a-z0-9_-]+)/ '$parsed_name';
}

## **`$imgproxy_preset`**
## Define `imgproxy` options for each preset name.
## You can view more details by following [their documentation](https://docs.imgproxy.net/usage/processing?#processing-options).
map $preset_name $imgproxy_preset
{
    default 'size:1600:0:0:0/preset:logo'; # preset:logo adds watermark to the image

    # Dynamic preset
    ~^max_w:(?<width>[0-9]+)$ 'size:${width}:0:0:0/preset:logo';

    # Static presets
    blurry   'size:320:320:1:0/blur:10/quality:50';
    small    'size:320:320:0:0/sharpen:0.3/preset:logo';
    medium   'size:640:640:0:0/preset:logo';
    thumb    'size:160:160:1:1/bg:ffffff/resizing_type:fill/sharpen:0.3';
    square   'size:500:500:0:1/bg:ffffff/resizing_type:fill/preset:logo';
    masked   'size:500:0:0:1/bg:ffffff/resizing_type:fill/preset:repeated_logo';
    download 'size:1600:0:0:0/preset:logo/return_attachment:1';
}

## **`$imgproxy_preset_query` (overriding presets with query string)**
## Override the `$imgproxy_preset` whenever `width` or `height` provided.
## But beware that dynamic image sizes are able to cause a denial-of-service attack by allowing an attacker to request multiple different image resizes.
map "$arg_width:$arg_height" $imgproxy_preset_query
{
    default '';

    # only width
    ~^(?<width>[0-9]+):$ '/size:${width}:0:1:0/bg:ffffff/resizing_type:fill';

    # only height
    ~^:(?<height>[0-9]+)$ '/size:0:${height}:1:0/bg:ffffff/resizing_type:fill';

    # both width and height
    ~^(?<width>[0-9]+):(?<height>[0-9]+)$ '/size:${width}:${height}:1:0/bg:ffffff/resizing_type:fill';
}

## **`$imgproxy_quality` (overriding photo quality with query string)**
## Control photo quality with query string. You can also add your custom settings.
map $arg_quality $imgproxy_quality
{
    default '';

    # if the given value is between 1 and 100, override the quality
    ~^(?<quality>[1-9][0-9]?|100)$ '/q:${quality}';

    # or receive some readable quality values
    low         '/q:30';
    clear       '/q:50';
    high        '/q:80';
    highest     '/q:100';
}

## **`$imgproxy_dpr` (controlling photo dimensions, aka Device Pixel Ratio)**
## This will multiplying the image dimensions according to this factor for HiDPI (Retina) devices.
map $http_user_agent@$http_dpr $imgproxy_dpr
{
    default '/dpr:1';

    # parse from DPR header
    ~@(?<dpr>[1-4])     '/dpr:${dpr}';

    # Put your rewrite rules for DPR settings from here.
    #> E.g. these lines will set custom DPR for smartphones.
    # ~iPhone|iPad|Mac    '/dpr:3';
    # ~Android            '/dpr:2';
}

##! **`$imgproxy_extension`**
## Detect WebP or AVIF supports from the request header `Accept`.
map $http_accept $imgproxy_extension
{
    default '';

    # uncomment this lines to enable WebP or AVIF compression
    # ~*webp  '@webp';
    # ~*avif  '@avif';
}

##! **`$imgproxy_type`**
## Detect WebP or AVIF supports from the request header `Accept`.
map $origin_server $imgproxy_type
{
    default '/plain';

    # omit the /plain/ prefix when the URL is base64-encoded
    ''      '';
}

## **`$imgproxy_options`**
## Generate final URL for `imgproxy` following [their documentation](https://docs.imgproxy.net/usage/processing).
## When URL query `?skip=1` is set, use another rule to skip `imgproxy` processing.
map $arg_skip $imgproxy_options
{
    default '/unsafe/${imgproxy_preset}${imgproxy_preset_query}${imgproxy_quality}${imgproxy_dpr}${imgproxy_type}/${origin_server}${origin_uri}${imgproxy_extension}';
    ~.+     '/unsafe${imgproxy_type}/${origin_server}${origin_uri}';
}

##! **`$imgproxy_rewrite`**
## Generate final URL for `imgproxy` following [their documentation](https://docs.imgproxy.net/usage/processing).
map $use_imgproxy $imgproxy_rewrite
{
    default '';
    1       '$imgproxy_options';
}

##! **`$debug_imgproxy_rewrite`**
## For debugging the value of `$imgproxy_rewrite`.
map $arg_debug $debug_imgproxy_rewrite
{
    default '';
    ~.+     '$imgproxy_rewrite';
}

## **`$fallback_uri`**
## Define fallback file to serve when the requested file is unavailable.
## E.g. `/noimage.jpg` or `/noimage_thumb.jpg`, which is stored in the folder `www/`.
map $preset_name $fallback_uri
{
    default '/noimage.jpg';
    thumb   '/noimage_thumb.jpg';
    # small   '/noimage_small.jpg';
    # medium  '/noimage_medium.jpg';
    # square  '/noimage_square.jpg';
}

# Extra: You also can set `$fallback_uri` using value of `$origin_uri`.
# map $origin_uri $fallback_uri
# {
#     default       '/noimage.jpg';
#     ~^/products/  '/default_product_image.jpg';
# }


================================================
FILE: nginx/Dockerfile
================================================
FROM nginx:alpine
ADD  nginx.conf  /etc/nginx/nginx.conf
ADD  dhparam.pem /etc/nginx/dhparam.pem

================================================
FILE: nginx/dhparam.pem
================================================
-----BEGIN DH PARAMETERS-----
MIIBDAKCAQEAyxsLdxJ06OSai23SZORDT+d7gR/KdmP9QXAFluwzWYwHsapXLSEW
qAsucR3eDqrg4rH2ScwOkPxDJ2Sj5Bz02H3K2PoxKDRTFTgR8OgglTVPwP5uQo8F
8LVIRV+PYQg4hwh5KF0g5YnrY8+dYdNyu8jrNhEQy+/x2OQhU4zsq6CCfHKOQdl9
/WQ+BeJWoNVPvJrK3VI/kXpzrpqGCrJ6si/eROSwaqyn9Ef4uZ0hmZlxgMGipbq3
LqD+NaA33J4fMq5D7tvmlbrE7ypHdkt/Lz2skjvTVEZerreE+zYKA1TehNCRMJ3B
E6JWcKJGEMXqyFuICbgPCqKXKCvEi6Cl3wIBAgICAOE=
-----END DH PARAMETERS-----


================================================
FILE: nginx/nginx.conf
================================================
################################################################################
# Ultra Image Server
# A production grade image processing server setup powered by imgproxy and nginx
#
# Author: Mai Nhut Tan <shin@shin.company>
# Copyright: 2021-2024 SHIN Company https://code.shin.company/
# URL: https://shinsenter.github.io/docker-imgproxy/
################################################################################

# Generated by nginxconfig.io
# https://www.digitalocean.com/community/tools/nginx#?domains.0.server.documentRoot=%2Fhome&domains.0.server.wwwSubdomain=true&domains.0.https.forceHttps=false&domains.0.https.hstsPreload=true&domains.0.https.certType=custom&domains.0.https.sslCertificate=%2Fetc%2Fnginx%2Fcerts%2Fserver.crt&domains.0.https.sslCertificateKey=%2Fetc%2Fnginx%2Fcerts%2Fserver.key&domains.0.php.php=false&domains.0.reverseProxy.reverseProxy=true&domains.0.routing.index=index.html&domains.0.routing.fallbackHtml=true&domains.0.logging.accessLog=true&domains.0.logging.errorLog=true&domains.0.restrict.getMethod=true&domains.0.restrict.headMethod=true&global.https.portReuse=true&global.https.ocspCloudflareType=both&global.https.ocspGoogleType=both&global.https.ocspOpenDns=false&global.https.ocspOpenDnsType=both&global.https.ocspQuad9Type=both&global.https.ocspVerisignType=both&global.https.letsEncryptCertRoot=%2Fetc%2Fnginx%2Fcerts%2Fletsencrypt%2Flive%2F&global.security.limitReq=true&global.reverseProxy.proxyConnectTimeout=10&global.reverseProxy.proxySendTimeout=30&global.reverseProxy.proxyReadTimeout=30&global.performance.assetsExpiration=max&global.performance.mediaExpiration=30d&global.performance.svgExpiration=max&global.performance.fontsExpiration=max&global.logging.logNotFound=true&global.logging.cloudflare=true&global.logging.trueClientIp=true&global.logging.cfIpCountry=true&global.logging.cdnLoop=true&global.nginx.user=nginx&global.nginx.pid=%2Fvar%2Frun%2Fnginx.pid&global.nginx.clientMaxBodySize=1&global.nginx.typesHashMaxSize=4096&global.nginx.typesHashBucketSize=1024&global.docker.dockerfile=true&global.docker.dockerCompose=true&global.tools.modularizedStructure=false&global.tools.symlinkVhost=false

user                 nginx;
pid                  /var/run/nginx.pid;
worker_processes     auto;
worker_rlimit_nofile 65535;

# Load modules
include              /etc/nginx/modules-enabled/*.conf;

events {
    multi_accept       on;
    worker_connections 65535;
}

http {
    charset                utf-8;
    sendfile               on;
    tcp_nopush             on;
    tcp_nodelay            on;
    server_tokens          off;
    types_hash_max_size    4096;
    types_hash_bucket_size 1024;
    client_max_body_size   1M;
    recursive_error_pages  on;

    # MIME
    include                mime.types;
    default_type           application/octet-stream;

    # Log Format
    log_format             cloudflare   '$remote_addr - $remote_user [$time_local] "$request" '
                                        '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
                                        '"$upstream_cache_status" "$imgproxy_rewrite" '
                                        '$http_cf_ray $http_cf_connecting_ip $http_true_client_ip '
                                        '$http_cf_ipcountry $http_cdn_loop';

    # Logging
    access_log             /var/log/nginx/access.log cloudflare;
    error_log              /var/log/nginx/error.log  error;

    # Limits
    limit_req_log_level    warn;
    limit_req_zone         $binary_remote_addr zone=login:10m rate=10r/m;

    # SSL Session
    ssl_session_timeout    1d;
    ssl_session_cache      shared:SSL:32m;
    ssl_session_tickets    off;

    # Diffie-Hellman parameter for DHE ciphersuites
    ssl_dhparam            /etc/nginx/dhparam.pem;

    # Mozilla Intermediate configuration
    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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

    # OCSP Stapling
    ssl_stapling           off;
    ssl_stapling_verify    off;
    resolver_timeout       10s;
    resolver               1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001] 8.8.8.8 8.8.4.4 [2001:4860:4860::8888] [2001:4860:4860::8844] valid=60s;

    ## Get file URI from non-normalized $request_uri
    map $request_uri $file_uri {
        default                     '$request_uri';
        ~^(?<parsed_path>.*)\?.*$   '$parsed_path';
    }

    # Connection header for WebSocket reverse proxy
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ""      close;
    }

    map $remote_addr $proxy_forwarded_elem {
        # IPv4 addresses can be sent as-is
        ~^[0-9.]+$        "for=$remote_addr";

        # IPv6 addresses need to be bracketed and quoted
        ~^[0-9A-Fa-f:.]+$ "for=\"[$remote_addr]\"";

        # Unix domain socket names cannot be represented in RFC 7239 syntax
        default           "for=unknown";
    }

    map $http_forwarded $proxy_add_forwarded {
        # If the incoming Forwarded header is syntactically valid, append to it
        "~^(,[ \\t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*([ \\t]*,([ \\t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem";

        # Otherwise, replace it
        default "$proxy_forwarded_elem";
    }

    # includes other config files, including imgproxy.conf
    include /etc/nginx/nginx.d/*.conf;

    # main server
    server {
        set $html_root /home;

        server_name                          _;
        root                                 $html_root;
        index                                index.html;

        # HTTP
        listen                               80 reuseport default_server;
        listen                               [::]:80 reuseport default_server;

        # SSL
        # listen                               443 ssl http2 reuseport;
        # listen                               [::]:443 ssl http2 reuseport;
        # ssl_certificate                      /etc/nginx/certs/server.crt;
        # ssl_certificate_key                  /etc/nginx/certs/server.key;

        # Proxy options
        proxy_http_version                      1.1;
        proxy_intercept_errors                  on;
        proxy_no_cache                          $arg_nocache;
        proxy_ssl_verify                        off;
        proxy_cache_background_update           off;
        proxy_cache_bypass                      $http_upgrade $arg_bypass $arg_debug $arg_nocache;
        proxy_cache_lock                        on;
        proxy_cache_lock_age                    5s;
        proxy_cache_lock_timeout                5s;
        proxy_cache_methods                     GET HEAD;
        proxy_cache_min_uses                    1;
        proxy_cache_revalidate                  on;
        proxy_cache_valid                       200 206 6h;
        proxy_cache_valid                       any 1m;
        proxy_cache_use_stale                   error timeout invalid_header updating
                                                http_429 http_500 http_502 http_503 http_504;

        # Proxy headers
        proxy_set_header Upgrade                $http_upgrade;
        proxy_set_header Connection             $connection_upgrade;
        proxy_set_header Host                   $host;
        proxy_set_header X-Real-IP              $remote_addr;
        proxy_set_header Forwarded              $proxy_add_forwarded;
        proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto      $scheme;
        proxy_set_header X-Forwarded-Host       $host;
        proxy_set_header X-Forwarded-Port       $server_port;

        # Proxy timeouts
        proxy_connect_timeout                   10s;
        proxy_send_timeout                      30s;
        proxy_read_timeout                      30s;

        # security headers
        add_header X-XSS-Protection             "1; mode=block" always;
        add_header X-Content-Type-Options       "nosniff" always;
        add_header Referrer-Policy              "no-referrer-when-downgrade" always;
        add_header Content-Security-Policy      "default-src 'self' http: https: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always;
        add_header Permissions-Policy           "interest-cohort=()" always;
        add_header Strict-Transport-Security    "max-age=31536000; includeSubDomains; preload" always;

        # gzip
        gzip            on;
        gzip_vary       on;
        gzip_proxied    any;
        gzip_comp_level 6;
        gzip_types      text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;

        # . files
        location ~ /\.(?!well-known) {
            deny all;
        }

        # restrict methods
        if ($request_method !~ ^(GET|HEAD)$) {
            return '405';
        }

        # favicon.ico
        location = /favicon.ico {
            log_not_found off;
            access_log    off;
        }

        # robots.txt
        location = /robots.txt {
            log_not_found off;
            access_log    off;
        }

        # proxy handlers
        error_page 418 = @imgproxy;
        error_page 421 = @remote;

        if ($imgproxy_rewrite)           { return 418; }
        if ($origin_server ~ ^https?://) { return 421; }

        # error pages
        error_page 401 403 404 405 422 429 500 501 502 503 504 =404 @fallback;

        # for imgproxy server
        location @imgproxy {
            # fallback to error image and error HTTP status
            error_page 401 403 404 405 422 429  =404 @fallback;
            error_page 500 501 502 503 504      =503 @fallback;
            add_header X-Cache $upstream_cache_status;
            add_header X-Debug $debug_imgproxy_rewrite always;

            proxy_cache     $imgproxy_cache;
            proxy_cache_key $imgproxy_rewrite;
            proxy_pass      http://upstream_imgproxy${imgproxy_rewrite};
        }

        # for remote servers
        location @remote {
            # fallback to error image and error HTTP status
            error_page 401 403 404 405 422 429  =404 @fallback;
            error_page 500 501 502 503 504      =503 @fallback;
            add_header X-Origin ${origin_server}${origin_uri} always;
            add_header X-Debug  $debug_imgproxy_rewrite always;

            etag off;
            proxy_pass ${origin_server}${origin_uri};
        }

        # for fallback
        location @fallback {
            internal;
            etag off;
            add_header X-Fallback $fallback_uri always;
            add_header X-Debug    $debug_imgproxy_rewrite always;

            try_files $fallback_uri $fallback_uri/;
        }

        # for localhost
        location / {
            try_files $origin_uri $uri $uri/ =404;
        }
    }
}
Download .txt
gitextract_5zpmuxpl/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── .vscode/
│   ├── conf2doc.sh
│   └── settings.json
├── README.md
├── SECURITY.md
├── _config.yml
├── docker-compose.yml
├── imgproxy.conf
└── nginx/
    ├── Dockerfile
    ├── dhparam.pem
    └── nginx.conf
Condensed preview — 12 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (58K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 64,
    "preview": "github: [shinsenter]\ncustom: [https://www.paypal.me/shinsenter]\n"
  },
  {
    "path": ".gitignore",
    "chars": 44,
    "preview": ".DS_Store\n.vscode/*.txt\n/cache\n/certs\n/logs\n"
  },
  {
    "path": ".vscode/conf2doc.sh",
    "chars": 725,
    "preview": "#!/bin/bash\n\nBASE=$(dirname $0)\nREADME=$BASE/../README.md\nPATH=\"/opt/homebrew/opt/grep/libexec/gnubin:$PATH\"\n\nUSAGE=\"$(\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 185,
    "preview": "{\n    \"editor.rulers\": [80, 100],\n    \"files.trimTrailingWhitespace\": true,\n    \"spellright.language\": [],\n    \"spellrig"
  },
  {
    "path": "README.md",
    "chars": 22146,
    "preview": "# Ultra Image Server\n\n![www/noimage_thumb.jpg](www/noimage_thumb.jpg)\n\n🌐 A production grade on-the-fly image processing "
  },
  {
    "path": "SECURITY.md",
    "chars": 274,
    "preview": "# Security Policy\n\n## Reporting a Vulnerability\n\nUse this section to tell people how to report a vulnerability.\n\nTell th"
  },
  {
    "path": "_config.yml",
    "chars": 26,
    "preview": "theme: jekyll-theme-hacker"
  },
  {
    "path": "docker-compose.yml",
    "chars": 9121,
    "preview": "################################################################################\n# Ultra Image Server\n# A production gra"
  },
  {
    "path": "imgproxy.conf",
    "chars": 10644,
    "preview": "################################################################################\n# Ultra Image Server\n# A production gra"
  },
  {
    "path": "nginx/Dockerfile",
    "chars": 96,
    "preview": "FROM nginx:alpine\nADD  nginx.conf  /etc/nginx/nginx.conf\nADD  dhparam.pem /etc/nginx/dhparam.pem"
  },
  {
    "path": "nginx/dhparam.pem",
    "chars": 428,
    "preview": "-----BEGIN DH PARAMETERS-----\nMIIBDAKCAQEAyxsLdxJ06OSai23SZORDT+d7gR/KdmP9QXAFluwzWYwHsapXLSEW\nqAsucR3eDqrg4rH2ScwOkPxDJ"
  },
  {
    "path": "nginx/nginx.conf",
    "chars": 11650,
    "preview": "################################################################################\n# Ultra Image Server\n# A production gra"
  }
]

About this extraction

This page contains the full source code of the shinsenter/docker-imgproxy GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 12 files (54.1 KB), approximately 15.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!