Fix: CloudFront Invalidation for Filenames with Apostrophes
In highly dynamic AWS environments, automated CI/CD pipelines or CMS systems often upload assets to S3 with special characters. A common issue occurs when attempting to invalidate a CloudFront cache for a filename containing an apostrophe (e.g., user's_profile.png).
The challenge stems from how the shell (Bash/Zsh) handles single quotes and how the AWS CLI parses the --paths argument. If not handled correctly, the CloudFront API receives a malformed path, leading to a failed invalidation or a silent failure where the cache is never cleared.
Exact Error Log
Section titled “Exact Error Log”When running a standard AWS CLI command, you may encounter a shell-level error or an API response like this:
# Attempting a direct CLI invalidationaws cloudfront create-invalidation --distribution-id E123456789ABCD --paths "/images/o'reilly.jpg"
# Potential Error OutputAn error occurred (InvalidArgument) when calling the CreateInvalidation operation:The parameter Paths contains a character that is not allowed or the request is malformed.Or, if using an unescaped string in a shell script:
bash: syntax error near unexpected token `)'Diagnostic Checklist
Section titled “Diagnostic Checklist”- S3 Key Verification: Confirm the object key in the S3 bucket actually contains the literal
'character and not a URL-encoded equivalent (like%27). - Shell Interpretation: Determine if your shell is stripping the apostrophe before passing the string to the AWS CLI.
- Encoding Standards: CloudFront requires paths to be UTF-8. If the apostrophe is part of a multi-byte character sequence, the invalidation will fail.
- IAM Permissions: Ensure the execution role has
cloudfront:CreateInvalidationpermissions for the specific distribution ARN. - Path Wildcards: Check if using a wildcard (e.g.,
/images/*) is a viable workaround, though this may incur higher costs if done frequently.
The Fix
Section titled “The Fix”Method 1: Using a JSON Invalidation Batch (Recommended)
Section titled “Method 1: Using a JSON Invalidation Batch (Recommended)”The most robust way to handle special characters in a Senior Cloud Architect’s workflow is to bypass shell escaping issues entirely by using a JSON input file. This ensures the apostrophe is treated as a literal string within the JSON parser.
- Create a file named
invalidation-batch.json:
{ "Paths": { "Quantity": 1, "Items": [ "/assets/images/o'reilly_book.png" ] }, "CallerReference": "invalidation-manual-$(date +%s)"}- Execute the invalidation using the
--invalidation-batchflag:
aws cloudfront create-invalidation \ --distribution-id EXXXXXXXXXXXXX \ --invalidation-batch file://invalidation-batch.jsonMethod 2: Proper Shell Escaping (Bash/Zsh)
Section titled “Method 2: Proper Shell Escaping (Bash/Zsh)”If you must use the CLI inline (e.g., inside a Jenkins or GitHub Actions runner), use double quotes around the path and escape the apostrophe, or wrap the whole path string carefully.
# Wrap the path in double quotes to allow the single quote to be literalaws cloudfront create-invalidation --distribution-id EXXXXXXXXXXXXX \ --paths "/products/men's-apparel.pdf"Method 3: Handling URL-Encoded Keys
Section titled “Method 3: Handling URL-Encoded Keys”In some environments, S3 objects are stored with URL-encoded names. If the file is literally named cat%27s.jpg on S3, you must invalidate that exact string. However, if it is named cat's.jpg, CloudFront usually expects the literal character.
If you are automating this via a Python/Boto3 Lambda function, ensure you are not double-encoding:
import boto3import time
client = boto3.client('cloudfront')
def invalidate_path(dist_id, path_with_apostrophe): # path_with_apostrophe = "/images/o'reilly.jpg" response = client.create_invalidation( DistributionId=dist_id, InvalidationBatch={ 'Paths': { 'Quantity': 1, 'Items': [path_with_apostrophe] }, 'CallerReference': str(time.time()) } ) return responseValidation
Section titled “Validation”To verify the invalidation was accepted and is processing:
aws cloudfront list-invalidations --distribution-id EXXXXXXXXXXXXX --max-items 1Look for the Status: "InProgress" or "Completed". If the status is Completed but the file still shows the old version, verify that the path provided exactly matches the object key in S3, including leading slashes.