# Deploy CDN Server > CDN-TMDb is our free, open source image server: it serves the images of your titles from your own domain and keeps them out of WordPress. This guide explains what you gain, compares it with the other image modes in real numbers, and shows how to deploy it and connect it to Dbmovies. Source: https://doothemes.com/docs/dbmovies/guides/deploy-cdn-server --- ## Overview Every page of a movie site is full of images: posters, backdrops, logos, episode images and photos of the cast. Where they come from matters more than anything else for the weight of your site. **CDN-TMDb** is our free, open source image server. You install it on a subdomain, such as `cdn.your-website.com`, and Dbmovies loads every image through it: 1. The first time a visitor asks for an image, CDN-TMDb downloads it from TMDb and stores it on its disk. 2. From then on, the server sends the stored file directly, as a static file, without running any code. Your images live on your own domain, and WordPress never has to handle them. For any site that plans to grow, it is the image mode we recommend. ## What you gain - **Your WordPress database stays light.** No image enters WordPress: no media library, no attachment rows, and no extra queries when a list of posters is built. - **Your WordPress server only serves pages.** Images are most of the weight of a page, and they go to the CDN server. WordPress answers fewer requests and uses less bandwidth. - **Nothing extra at import.** The importer does not download, convert or resize any image, so importing stays fast. - **Your images on your own domain,** with a browser cache of one year: a visitor who comes back does not download them again. - **Independent from TMDb.** Once stored, an image keeps loading if TMDb is slow, down, or blocked on a visitor's network. - **Images that TMDb removes are kept.** TMDb removes images from time to time. CDN-TMDb notices it and keeps its copy forever. - **One server for all your sites.** Each image is downloaded once and serves every site you connect. - **Under your control.** You can allow only your own domains, see how many images are stored and clean them up. ## Compare the image modes Dbmovies can show images in three ways (see [Import images](https://doothemes.com/docs/dbmovies/settings/import-images)): - **TMDb directly**, the default: images load from `image.tmdb.org`. - **Your CDN:** images load from your CDN-TMDb server. - **Upload to your server:** the importer copies the poster of each title, and the image of each episode, to your media library. ### What each uploaded image costs We measured it on a Dbmovies site with Wovie. Image weights are the average of real posters and episode images from TMDb. Database rows and processing time come from uploading test images the same way the importer does. | For each uploaded image | Cost | |---|---| | Database rows | 4: 1 in `wp_posts` and 3 in `wp_postmeta`, about 1 KB of data | | Files on your server | 3: the image and two smaller sizes | | Processing at import | About 0.7 seconds, plus the download | | Space for a poster, with its sizes | About 325 KB | | Space for an episode image, with its sizes | About 90 KB | | Database queries to show it in a list, without Redis | 2 | ### For a whole catalog A catalog of **10,000 movies and TV shows** and **100,000 episodes**, with the uploads of posters and episode images turned on: | | TMDb directly | Your CDN | Upload to your server | |---|---|---|---| | Rows added to your database | 0 | 0 | **440,000** | | Data added to your database | 0 | 0 | **About 120 MB**, plus indexes | | Files on your WordPress server | 0 | 0 | **330,000** | | Space on your WordPress server | 0 | 0 | **About 12 GB** | | Extra processing to import the catalog | None | None | **About 21 hours**, plus the downloads | | Extra database queries for a list of 20 posters, without Redis | 0 | 0 | **40** | | Image requests your WordPress server answers for that list | 0 | 0 | **20**, about 1.6 MB | | Size of your backups | Unchanged | Unchanged | **About 12 GB more** | | Images on your own domain | No | **Yes** | Yes | | If TMDb is slow or down | Images do not load | **Stored images keep loading** | Images keep loading | | If TMDb removes an image | It is lost | **It is kept**, if your CDN already stored it | It is kept | Some notes on the numbers: - **The space is measured as JPEG.** With the WebP converter, which is on by default, the files weigh less, but the number of files and database rows does not change. - **Backdrops, logos and cast photos** are never uploaded: in every mode they come from TMDb or from your CDN. - **The extra queries** are the lookups of each image in the database. With [Redis](https://doothemes.com/docs/dbmovies/guides/redis-cache) they become lookups in memory, but the media library still has to be stored, backed up and served. ### How to read it - **TMDb directly and your CDN** are equally light for WordPress: neither adds anything to your database or your server. - **Your CDN** adds what TMDb directly cannot give you: your own domain, a long browser cache, independence from TMDb and the images TMDb removes. - **Uploading** gives you control too, but its costs grow with every title you import, and they stay in your database, your disk and your backups. ## What you need - **A server for the CDN** with Apache, PHP 8.1 or newer and the `curl` extension. - Apache needs `mod_rewrite` and `mod_expires`, and we recommend `mod_headers`. - It does not need a database. A small VPS or a hosting account is enough, and it can be the same server as your site. - LiteSpeed reads the same `.htaccess`. With nginx in front of Apache, as in Plesk, add a few rules to nginx too: see [If nginx serves your files](#if-nginx-serves-your-files). On nginx alone, the rules of `.htaccess` have to be translated. - **A subdomain with HTTPS,** such as `cdn.your-website.com`. - **Disk space** for the images your visitors view. CDN-TMDb stores only the images that are requested, and deletes the ones nobody viewed in 30 days. - **SSH access** to generate the access token and schedule the cleanup. Without it, you can edit the files by hand. ## Deploy the server 1. **Create the subdomain** in your hosting panel or your DNS, with an empty folder as its document root, and turn on its HTTPS certificate. 2. **Put CDN-TMDb in that folder.** With SSH: ```bash cd /path/to/cdn.your-website.com git clone https://github.com/doothemes/CDN-TMDb.git . ``` Without SSH, download the latest version as a ZIP: **[Download CDN-TMDb](https://github.com/doothemes/CDN-TMDb/archive/refs/heads/main.zip)**. Extract it and upload the content of its `CDN-TMDb-main` folder to the document root, not the folder itself. Include the hidden files, such as `.htaccess`: some FTP programs and file managers hide them. You can see what changed in each version in the [releases](https://github.com/doothemes/CDN-TMDb/releases). 3. **Create the configuration file:** ```bash cp .env.example .env ``` 4. **Generate the access token.** This command writes a random `API_SECRET` into `.env` and shows it. Copy it: you need it to connect Dbmovies. ```bash php rotate-token.php ``` Without SSH, edit `.env` and set `API_SECRET` to a long random value. 5. **Review the other settings** in `.env`. The defaults fit most sites: | Setting | What it does | Default | |---|---|---| | `MAX_INACTIVE_DAYS` | Days without visits before the cleanup deletes an image | `30` | | `CORS_ORIGIN` | Which sites can load the images from code. `*` allows all | `*` | | `NEGATIVE_CACHE_TTL` | Seconds to remember an image TMDb does not have, so it is not asked again | `3600` | | `LOG_MAX_SIZE_MB` | Size of a log before it is rotated | `5` | | `LOG_KEEP_FILES` | How many old logs are kept | `5` | | `MAX_HEAD_REQUESTS_PER_RUN` | How many images each cleanup checks with TMDb, at most | `500` | 6. **Schedule the cleanup.** Add this line to the crontab of the server. It runs every day at 3:00: ```bash 0 3 * * * php /path/to/cdn.your-website.com/cron.php >> /var/log/cdn-cleanup.log 2>&1 ``` The cleanup deletes the images nobody viewed in `MAX_INACTIVE_DAYS`. Before deleting one, it asks TMDb if it still has it. If TMDb no longer has it, the image is marked as **archival** and kept forever. 7. **Optional: allow only your sites.** The `.htaccess` has an anti-hotlink block, off by default. To turn it on, remove the `###` at the start of its lines and write your domains. Visits with no referrer, such as apps and search engines, are still allowed. ### If nginx serves your files On some servers, nginx sits in front of Apache and sends existing files on its own, without reading `.htaccess`. In Plesk, that happens with **Smart static files processing** or **Serve static files directly by nginx**. Then the rules of CDN-TMDb that block its private files do not apply, and you need to add them to nginx too: ```nginx location ~ /\.(?!well-known/) { deny all; } location ^~ /logs/ { deny all; } location ~ \.(tmp|lock)$ { deny all; } ``` In Plesk, paste them in **Apache & nginx Settings › Additional nginx directives** of the subdomain. They block the same files as `.htaccess`: hidden files and folders such as `.env` and `.git`, the `logs` folder, and temporary files. They let `.well-known` through, which Let's Encrypt uses to renew your certificate. ### Check the server 1. Open the test image in your browser: `https://cdn.your-website.com/t/p/w92/pB8BM7pdSp6B6Ih7QZ4DrQ3PmJK.jpg`. You see the poster of *Fight Club*. 2. Open `https://cdn.your-website.com/.env`, or run `curl -I https://cdn.your-website.com/.env` in a terminal. You must get an error: **403 Forbidden**. CDN-TMDb blocks its configuration, its logs and the `.git` folder. If you see the content of the file instead: - your copy of CDN-TMDb is older than 1.2.0: update it (see [Keep it running](#keep-it-running)); - or nginx sends the file before Apache reads `.htaccess`: add the rules of [If nginx serves your files](#if-nginx-serves-your-files). In both cases, check again until you get 403, then generate a new token with `php rotate-token.php` and paste it in Dbmovies: the old one was visible. ## Connect Dbmovies 1. Go to **Dbmovies › Settings › Data Importer › Import Images**. 2. In **CDN server**, enter the address of your CDN, such as `https://cdn.your-website.com`, without `/t/p/` and without a slash at the end. 3. In **CDN access token**, paste the `API_SECRET` of your server. The token stays in your WordPress admin: visitors never see it. 4. Click **Save**. Dbmovies loads the test image through your CDN. If it fails, the address is not saved and a message tells you why. 5. Click **Test connection** to see how many images your CDN stores and how much space they use. [SCREENSHOT_HERE: Dbmovies › Settings › Data Importer › Import Images, with CDN server, CDN access token and the result of Test connection] From then on, every image of your site loads from your CDN. If you use a page cache, clear it so the pages are built with the new addresses. **If you were uploading images to your server,** uncheck **Upload featured images**, so new imports stop copying them. Titles that already have an uploaded featured image keep showing it, because a featured image comes before the CDN. ## Keep it running | Task | How | |---|---| | See what is stored | **Test connection** in Dbmovies, or `curl -H "X-Api-Key: YOUR-TOKEN" https://cdn.your-website.com/get_stats` | | Free space | The daily cleanup does it on its own. **Clear cache** in Dbmovies deletes every stored image except the archival ones | | Start from zero | **Delete all** in Dbmovies deletes everything, archival images included. They cannot be recovered | | Change the token | Run `php rotate-token.php` on the server, then paste the new token in **CDN access token** | | Update CDN-TMDb | In its folder, run `git pull`. Your `.env`, your stored images and your logs are kept. Without SSH, [download the latest version](https://github.com/doothemes/CDN-TMDb/archive/refs/heads/main.zip) and replace the files, except `.env` and the `t/` and `logs/` folders | | Find download errors | Read `logs/error.log` on the server | | Move to another server | Copy the whole folder, including `t/`, where the images are stored. Archival images exist only there | [SCREENSHOT_HERE: The result of Test connection, with the number of stored images, archival images and the space used] ## One CDN for several sites Connect all your Dbmovies sites to the same CDN, with the same address and token in each one. Each image is downloaded once and serves all of them. If you turn on the anti-hotlink block, add the domain of each site. ## Troubleshooting When you save the CDN server, Dbmovies shows one of these messages if something is wrong: | Message | What to do | |---|---| | *Enter the full address of the CDN server, starting with https://.* | Write the full address, such as `https://cdn.your-website.com` | | *The address … could not be found.* | The subdomain does not exist yet, or its DNS has not spread. Wait a few minutes and try again | | *The CDN server must use https://* | Turn on HTTPS for the subdomain: your site uses HTTPS and browsers would block the images | | *The CDN server did not respond: …* | The server is down, a firewall blocks it, or its certificate is not valid | | *The CDN server answered with HTTP 404 instead of the test image.* | The `.htaccess` is not active: check that `mod_rewrite` is on and that the folder allows `.htaccess` files (`AllowOverride All`) | | *The CDN server answered with HTTP 500 instead of the test image.* | `.env` is missing, the PHP version is older than 8.1, or the `curl` extension is missing | | *The CDN server responded, but not with an image.* | The address opens a different site. Check that the document root of the subdomain is the CDN-TMDb folder |