> ## Documentation Index
> Fetch the complete documentation index at: https://cloudinary.com/documentation/llms.txt
> Use this file to discover all available pages before exploring further.

# Optimize PDFs


Whether you're delivering PDFs from your website, or using Cloudinary to store and share them, PDF compression may be of interest to you. By compressing PDFs, especially if they're image-rich, you can benefit from big storage savings and faster download speeds.

> **INFO**: PDF optimization is currently available to certain customers only. If you'd like to enable it for your product environment, please [contact support](https://support.cloudinary.com/hc/en-us/requests/new).

## Automatic quality for PDFs

You can optimize the size of your PDFs automatically:

* [On delivery](#optimize_pdfs_on_delivery) from Cloudinary
* [On upload](#optimize_pdfs_on_upload) to Cloudinary
* [On demand](#optimize_previously_uploaded_pdfs) for PDFs that already exist in Cloudinary

In each case, use the [automatic quality](transformation_reference#q_auto) transformation (`q_auto` in URLs), which applies a certain amount of compression to your PDFs, while maintaining visual quality.

> **NOTES**:
>
> * To optimize PDFs, ensure they're [uploaded](upload_parameters#uploading_pdfs) with the asset type `image`.

> * Quality types such as `best`, `good`, `eco`, etc., aren't yet supported when applying automatic quality to PDFs. 

> * For Cloudinary to optimize a PDF, it must be 300 pages or fewer and 200 MB or less in size. If it exceeds either of these limits or takes longer than 120 seconds to process, Cloudinary returns the original instead.

> * Some PDFs, even below these limits, may not support optimization and size reduction. In these cases, Cloudinary returns the original.

> * PDFs with images typically benefit from better optimization than those without.

> * PDF optimization is intended mainly for on-screen viewing (in a browser or PDF viewer). For high-quality printing, it's best to avoid optimizing the PDF.

> * There's a [special transformation count](transformation_counts#multi_page_image_files) for applying automatic quality to PDFs.

> * [Default image quality](image_optimization#default_image_quality) doesn't apply to PDFs.

### Optimize PDFs on delivery

> **INFO**:
>
> To reduce the chances of malware or other potentially harmful files being distributed via the Cloudinary domain, PDFs (and certain other file formats) are [blocked for delivery](image_delivery_options#blocked_delivery_formats_for_security) by default for FREE accounts. To deliver PDFs, make sure the **Allow delivery of PDF and ZIP files** option is selected in the **Security** page of the Console Settings.

If you're storing your original uncompressed PDFs in your Cloudinary product environment, you can deliver compressed PDFs on the fly to your end users without affecting the original copy.

To deliver optimized PDFs, apply the `q_auto` transformation parameter to your delivery URL, either directly, or using one of our SDKs, as shown:

![Optimized PDF](https://res.cloudinary.com/cld-docs/image/upload/q_auto/39_Acacia_Road_Brochure.pdf "with_image:false, with_url:true, with_code:true")

In this case, the [original PDF](https://res.cloudinary.com/cld-docs/image/upload/39_Acacia_Road_Brochure.pdf "popup: true") is 1.8 MB and the [optimized PDF](https://res.cloudinary.com/cld-docs/image/upload/q_auto/39_Acacia_Road_Brochure.pdf "popup:true") is 1.1 MB.

### Optimize PDFs on upload

If you want to save on storage, you can optimize PDFs on upload using an [incoming transformation](eager_and_incoming_transformations#incoming_transformations). In this case, the compressed version of the PDF is stored in your product environment.

> **NOTE**: When uploading a PDF with an incoming transformation that includes automatic quality, you must set the `async` parameter to `true` for the PDF to be processed.

```multi
|ruby
Cloudinary::Uploader.upload("39_Acacia_Road_Brochure_incoming.pdf", 
  transformation: { quality: "auto" },
  async: true)
   
|php_2
$cloudinary->uploadApi()->upload("39_Acacia_Road_Brochure_incoming.pdf", 
  [
    "transformation" => ["quality" => "auto"],
    "async" => true
  ]);
     
|python
cloudinary.uploader.upload("39_Acacia_Road_Brochure_incoming.pdf", 
  transformation={"quality": "auto"},
  async_=True)

|nodejs
cloudinary.v2.uploader
.upload("39_Acacia_Road_Brochure_incoming.pdf",
  { 
    transformation: { quality: "auto" },
    async: true
  })
.then(result=>console.log(result)); 
  
|java
cloudinary.uploader().upload("39_Acacia_Road_Brochure_incoming.pdf",
  ObjectUtils.asMap(
    "transformation", new Transformation().quality("auto"),
    "async", true));

|csharp
var uploadParams = new ImageUploadParams() {
  File = new FileDescription(@"39_Acacia_Road_Brochure_incoming.pdf"),
  Transformation = new Transformation().Quality("auto"),
  Async = true};
var uploadResult = cloudinary.Upload(uploadParams); 

|go
resp, err := cld.Upload.Upload(ctx, "39_Acacia_Road_Brochure_incoming.pdf", uploader.UploadParams{
  Transformation: "q_auto",
  Async:          api.Bool(true)})

|android
MediaManager.get().upload("39_Acacia_Road_Brochure_incoming.pdf")
  .option("transformation", new Transformation().quality("auto"))
  .option("async", true)
  .dispatch();

|swift
let params = CLDUploadRequestParams()
  .setTransformation(
    CLDTransformation().setQuality("auto"))
  .setAsync(true)
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(
  url: "39_Acacia_Road_Brochure_incoming.pdf", params: params)

|cli
cld uploader upload 39_Acacia_Road_Brochure_incoming.pdf transformation='[{"quality": "auto"}]' async=true

|curl
curl https://api.cloudinary.com/v1_1/demo/image/upload -X POST --data 'file=39_Acacia_Road_Brochure_incoming.pdf&transformation=q_auto&async=true&timestamp=173719931&api_key=436464676&signature=a788d68f86a6f868af'
```  

Response:

```json
{
  "asset_id": "a5f6b530d5047595ecc6eb9749b964e2",
  "public_id": "39_Acacia_Road_Brochure_incoming",
  "version": 1743089313,
  "version_id": "a7fda692479094917db557c39377236d",
  "signature": "c1b5400f382ebdbabff00e85d7218eda8b10dca9",
  "width": 595,
  "height": 841,
  "format": "pdf",
  "resource_type": "image",
  "created_at": "2025-03-27T15:28:33Z",
  "tags": [],
  "pages": 4,
  "bytes": 1115921,
  "type": "upload",
  "etag": "8efd8251bf8ee5993f4cac036d4265f8",
  "placeholder": false,
  "url": "http://res.cloudinary.com/cld-docs/image/upload/v1743089313/39_Acacia_Road_Brochure_incoming.pdf",
  "secure_url": "https://res.cloudinary.com/cld-docs/image/upload/v1743089313/39_Acacia_Road_Brochure_incoming.pdf",
  "asset_folder": "",
  "display_name": "39_Acacia_Road_Brochure_incoming",
  "metadata": {
    "my_metadata": [
      "value1",
      "value2"
    ]
  },
  "original_filename": "39_Acacia_Road_Brochure_incoming",
  "api_key": "614335564976464"
}
```

### Optimize previously uploaded PDFs

If you've got original, uncompressed PDFs already in your Cloudinary product environment, and you want to save some storage, you can follow these steps to overwrite an original PDF with an optimized PDF:

1. Use the original PDF's URL as the upload source.
1. Apply the automatic quality transformation as an incoming transformation.
1. Set the public ID to match the existing PDF.
1. Include the overwrite and invalidate parameters.
1. Set the async parameter to true.

> **NOTE**: When uploading a PDF with an incoming transformation that includes automatic quality, you must set the `async` parameter to `true` for the PDF to be processed.

For example:

```multi
|ruby
Cloudinary::Uploader.upload("https://res.cloudinary.com/cld-docs/image/upload/39_Acacia_Road_Brochure_original.pdf", 
  public_id: "39_Acacia_Road_Brochure_original",
  overwrite: true,
  invalidate: true,
  transformation: { quality: "auto" },
  async: true)

|php_2
$cloudinary->uploadApi()->upload("https://res.cloudinary.com/cld-docs/image/upload/39_Acacia_Road_Brochure_original.pdf", 
  [
    "public_id" => "39_Acacia_Road_Brochure_original",
    "overwrite" => true,
    "invalidate" => true,
    "transformation" => ["quality" => "auto"],
    "async" => true
  ]);

|python
cloudinary.uploader.upload("https://res.cloudinary.com/cld-docs/image/upload/39_Acacia_Road_Brochure_original.pdf",
  public_id="39_Acacia_Road_Brochure_original",
  overwrite=True,
  invalidate=True,
  transformation={"quality": "auto"},
  async_=True)

|nodejs
cloudinary.v2.uploader
.upload("https://res.cloudinary.com/cld-docs/image/upload/39_Acacia_Road_Brochure_original.pdf", {
  public_id: "39_Acacia_Road_Brochure_original",
  overwrite: true,
  invalidate: true,
  transformation: { quality: "auto" },
  async: true
})
.then(result => console.log(result));

|java
cloudinary.uploader().upload("https://res.cloudinary.com/cld-docs/image/upload/39_Acacia_Road_Brochure_original.pdf",
  ObjectUtils.asMap(
    "public_id", "39_Acacia_Road_Brochure_original",
    "overwrite", true,
    "invalidate", true,
    "transformation", new Transformation().quality("auto"),
    "async", true
  ));

|csharp
var uploadParams = new ImageUploadParams() {
  File = new FileDescription("https://res.cloudinary.com/cld-docs/image/upload/39_Acacia_Road_Brochure_original.pdf"),
  PublicId = "39_Acacia_Road_Brochure_original",
  Overwrite = true,
  Invalidate = true,
  Transformation = new Transformation().Quality("auto"),
  Async = true
};
var uploadResult = cloudinary.Upload(uploadParams);

|go
resp, err := cld.Upload.Upload(ctx, "https://res.cloudinary.com/cld-docs/image/upload/39_Acacia_Road_Brochure_original.pdf", uploader.UploadParams{
  PublicID:       "39_Acacia_Road_Brochure_original",
  Overwrite:      true,
  Invalidate:     true,
  Transformation: "q_auto",
  Async:          api.Bool(true),
})

|android
MediaManager.get().upload("https://res.cloudinary.com/cld-docs/image/upload/39_Acacia_Road_Brochure_original.pdf")
  .option("public_id", "39_Acacia_Road_Brochure_original")
  .option("overwrite", true)
  .option("invalidate", true)
  .option("transformation", new Transformation().quality("auto"))
  .option("async", true)
  .dispatch();

|swift
let params = CLDUploadRequestParams()
  .setPublicId("39_Acacia_Road_Brochure_original")
  .setOverwrite(true)
  .setInvalidate(true)
  .setTransformation(CLDTransformation().setQuality("auto"))
  .setAsync(true)
var mySig = MyFunction(params)
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(
  url: "https://res.cloudinary.com/cld-docs/image/upload/39_Acacia_Road_Brochure_original.pdf", params: params)

|cli
cld uploader upload https://res.cloudinary.com/cld-docs/image/upload/39_Acacia_Road_Brochure_original.pdf public_id="39_Acacia_Road_Brochure_original" overwrite=true invalidate=true transformation='[{"quality": "auto"}]' async=true

|curl
curl https://api.cloudinary.com/v1_1/demo/image/upload -X POST \
--data 'file=https://res.cloudinary.com/cld-docs/image/upload/39_Acacia_Road_Brochure_original.pdf&public_id=39_Acacia_Road_Brochure_original&overwrite=true&invalidate=true&transformation=q_auto&async=true&timestamp=173719931&api_key=436464676&signature=a788d68f86a6f868af'
```

This applies the automatic quality transformation, overwrites the existing asset, and invalidates any cached versions on the CDN.

> **READING**:
>
> * Discover how to [deliver specific pages of a PDF as a new PDF or as images](paged_and_layered_media#delivering_content_from_pdf_files).

> * Learn how to [create PDFs from a set of images](create_pdf_files_from_images).

### Troubleshooting PDF optimization

If the PDF after optimization is the same size as the original, it may be because:

* The optimization is still in progress. When you first request a PDF with `q_auto`, Cloudinary has 30 seconds to complete the optimization. If it doesn't finish in time, Cloudinary returns the original PDF and continues processing for up to another 90 seconds in the background. The original PDF is cached on the CDN for five minutes. During this five-minute period, all requests will receive the original PDF. Once the cache expires and optimization has completed successfully, subsequent requests for the PDF with `q_auto` will return the optimized version.
* The PDF exceeds 300 pages or 200 MB, which is beyond the optimization limits.
* Processing takes longer than 120 seconds, which exceeds the optimization time limit.
* The PDF is already as optimized as it can be.