A production-ready Serverless Framework plugin that automatically creates an AWS CloudFront distribution serving static web content from S3 with optional API Gateway routing.
- Custom Domain Support - Set up custom domains for both S3-hosted sites and API Gateway
- Free SSL/TLS - Automatic SSL certificate management via AWS Certificate Manager
- No CORS Issues - Serve static content and API from the same domain
- CDN Caching - CloudFront caching reduces Lambda invocations and API Gateway traffic
- Enhanced Monitoring - Comprehensive CloudWatch statistics including bandwidth metrics
- Access Logging - Real-world Apache-style access logs
- WAF Integration - Built-in Web Application Firewall support for threat protection
- Flexible Deployment - Works with or without API Gateway functions
- 96.8% Test Coverage - Comprehensive test suite ensuring reliability
- Security Hardened - Protected against command injection and path traversal attacks
- Modern Dependencies - Up-to-date, secure dependency versions
- Path Traversal Protection - Built-in safeguards against directory traversal exploits
- Serverless Framework - Install globally:
npm install -g serverless
- AWS Account - Configure your AWS credentials
- Node.js - Version 4.0 or higher (recommend 18+ for best security)
Install the plugin as a development dependency:
npm install --save-dev @360mediadirect/fullstack-serverless- All fullstack-serverless configuration parameters are optional - e.g. don't provide ACM Certificate ARN to use default CloudFront certificate (which works only for default cloudfront.net domain).
- This plugin does not set-up automatically Route53 for newly created CloudFront distribution. After creating CloudFront distribution, manually add Route53 ALIAS record pointing to your CloudFront domain name.
- First deployment may be quite long (e.g. 10 min) as Serverless is waiting for CloudFormation to deploy CloudFront distribution.
Add the plugin to your serverless.yml:
plugins:
- '@360mediadirect/fullstack-serverless'
custom:
fullstack:
bucketName: webapp-deploy # Required: Unique S3 bucket name
distributionFolder: client/dist # Optional: Path to client assets (default: client/dist)
domain: my-custom-domain.com # Optional: Custom domain
certificate: arn:aws:acm:us-east-1:... # Optional: ACM certificate ARN for SSL
indexDocument: index.html # Optional: Index document (default: index.html)
errorDocument: error.html # Optional: Error document (default: error.html)
singlePageApp: false # Optional: SPA routing support (default: false)
compressWebContent: true # Optional: Enable compression (default: true)
apiPath: api # Optional: API path prefix (default: api)
minimumProtocolVersion: TLSv1.2_2021 # Optional: Minimum TLS version (recommended: TLSv1.2_2021)
priceClass: PriceClass_100 # Optional: CloudFront price class
waf: arn:aws:wafv2:... # Optional: WAF ARN for protection
logging:
bucket: my-bucket.s3.amazonaws.com
prefix: my-prefix
invalidationPaths: # Optional: CloudFront invalidation paths
- /index.html
- /error.html1. Create your website folder
Create a website folder in the root directory of your Serverless project:
mkdir -p client/dist
echo "<!DOCTYPE html><html><body><h1>Go Serverless!</h1></body></html>" > client/dist/index.html
echo "<!DOCTYPE html><html><body><h1>Error Page</h1></body></html>" > client/dist/error.html2. Deploy your stack
serverless deployβ±οΈ Note: First deployment takes ~10 minutes as CloudFormation creates the CloudFront distribution.
The plugin will output your CloudFront distribution URL when complete.
--no-delete-contents.
# Deploy everything (infrastructure + client)
serverless deploy
# Deploy only client files
serverless client deploy
# Deploy without deleting existing S3 content
serverless client deploy --no-delete-contents
# Deploy without generating client (skip clientCommand)
serverless client deploy --no-generate-client
# Remove the deployed stack
serverless client removebucketName
required
custom:
fullstack:
...
bucketName: [unique-s3-bucketname]
...Use this parameter to specify a unique name for the S3 bucket that your files will be uploaded to.
distributionFolder
optional, default: client/dist
custom:
fullstack:
...
distributionFolder: [path/to/files]
...Use this parameter to specify the path that contains your website files to be uploaded. This path is relative to the path that your serverless.yaml configuration files resides in.
apiPath
optional, default: api
custom:
fullstack:
...
apiPath: api
...Use this parameter to specify the path prefix your API Gateway methods will be available through on your CloudFront distribution (custom domain)
- If
httpevents are defined,apiPathmust be included in the path for the lambdas you want exposed through CloudFront (your custom domain). Not all your methods need to be exposed through CloudFront. For some things, esp. those that are not public facing (eg. third party web hooks) you may want to use the ApiGateway URL and not expose them through CloudFront to control access and cost.
functions:
message:
handler: message.handler
timeout: 30
events:
- http:
path: ${self:custom.fullstack.apiPath}/message
method: post
integration: lambdaapiGatewayRestApiId
optional, default: not set
custom:
fullstack:
...
apiGatewayRestApiId: a12bc34df5
...This is only needed if "Api Gateway Rest Api" is not part of the same serverless template and the API id is not defined in provider -> apiGateway section.
The id can be found in API Gateway url. For example, if your Rest API url is https://a12bc34df5.execute-api.eu-central-1.amazonaws.com, API id will be a12bc34df5.
certificate
optional, default: not set
custom:
fullstack:
...
certificate: arn:aws:acm:us-east-1:...
...Use this parameter to specify ARN for the SSL cert to use form AWS CertificateManager
indexDocument
optional, default: index.html
custom:
fullstack:
...
indexDocument: [file-name.ext]
...The name of your index document inside your distributionFolder. This is the file that will be served to a client visiting the base URL for your website.
domain
optional, default: not set
custom:
fullstack:
...
domain: my-custom-domain.com
...domain can be a list, if you want to add more domains:
custom:
fullstack:
...
domain:
- my-custom-domain.com
- secondary-custom-domain.com
...The custom domain for your fullstack serverless app.
errorDocument
optional, default: error.html
custom:
fullstack:
...
errorDocument: [file-name.ext]
...The name of your error document inside your distributionFolder. This is the file that will be served to a client if their initial request returns an error (e.g. 404). For an SPA, you may want to set this to the same document specified in indexDocument so that all requests are redirected to your index document and routing can be handled on the client side by your SPA.
objectHeaders
optional, no default
custom:
fullstack:
...
objectHeaders:
ALL_OBJECTS:
- name: header-name
value: header-value
...
specific-directory/:
- name: header-name
value: header-value
...
specific-file.ext:
- name: header-name
value: header-value
...
... # more file- or folder-specific rules
...Use the objectHeaders option to set HTTP response headers be sent to clients requesting uploaded files from your website.
Headers may be specified globally for all files in the bucket by adding a name, value pair to the ALL_OBJECTS property of the objectHeaders option. They may also be specified for specific folders or files within your site by specifying properties with names like specific-directory/ (trailing slash required to indicate folder) or specific-file.ext, where the folder and/or file paths are relative to distributionFolder.
Headers with more specificity will take precedence over more general ones. For instance, if 'Cache-Control' was set to 'max-age=100' in ALL_OBJECTS and to 'max-age=500' in my/folder/, the files in my/folder/ would get a header of 'Cache-Control: max-age=500'.
singlePageApp
optional, default: false
custom:
fullstack:
...
singlePageApp: true
...If true 403 errors will be rerouted (missing assets) to your root index document to support single page apps like React and Angular where the js framework handles routing
invalidationPaths
optional, default: ['/*']
custom:
fullstack:
...
invalidationPaths:
- /index.html
- /error.html
...Custom invalidationPaths for cloudfront in case your frontend framework uses filename hashing
compressWebContent
optional, default: true
custom:
fullstack:
...
compressWebContent: true
...Instruct CloudFront to use compression when serving web content, see Serving Compressed Files in the Amazon CloudFront Developer Guide.
clientCommand
optional, default: not set
custom:
fullstack:
...
clientCommand: [command to generate your client (e.g. gulp dist)]
...Command to generate the client assets. Defaults to doing nothing
clientSrcPath
optional, default: not set
custom:
fullstack:
...
clientSrcPath: [path/to/your/client]
...The path to where you want to run the clientCommand
waf
optional, default: not set
custom:
fullstack:
...
waf: [web application firewall ARN]
...Web Application Firewall support - enable AWS WAF to protect your API from security threats
logging
optional, default: not set
custom:
fullstack:
...
logging:
bucket: my-bucket.s3.amazonaws.com
prefix: my-prefix
...Real world access log - out of the box, API Gateway currently does not provide any kind of real "apache-like" access logs for your invocations
priceClass
optional, default: PriceClass_All
custom:
fullstack:
...
priceClass: PriceClass_100
...CloudFront PriceClass - can be PriceClass_All (default), PriceClass_100 or PriceClass_200
minimumProtocolVersion
optional, default: TLSv1
custom:
fullstack:
...
minimumProtocolVersion: TLSv1.2_2018
...Set minimum SSL/TLS protocol version - TLSv1_2016, TLSv1.1_2016, TLSv1.2_2018 or SSLv3
- The minimum SSL/TLS protocol that CloudFront uses to communicate with viewers
- The cipher that CloudFront uses to encrypt the content that it returns to viewers
noConfirm
optional, default: false
custom:
fullstack:
...
noConfirm: true
...Use this parameter if you do not want a confirmation prompt to interrupt automated builds. If either this or --no-confirm CLI parameter is true the confirmation prompt will be disabled.
origins
optional, default: not set
custom:
fullstack:
...
origins:
- Id: Media
DomainName:
Fn::GetAtt:
- MediaBucket
- DomainName
S3OriginConfig:
OriginAccessIdentity:
Fn::Join:
- ''
- - origin-access-identity/cloudfront/
- { Ref: S3OriginAccessIdentity }
...Use this parameter if you want to add additional origins to the CloudFormation resources.
defaultCacheBehavior
optional, default: not set
custom:
fullstack:
...
defaultCacheBehavior:
MinTTL: 3600
...cacheBehaviors
optional, default: not set
custom:
fullstack:
...
cacheBehaviors:
- TargetOriginId: Media
PathPattern: media/*
AllowedMethods:
- GET
- HEAD
- OPTIONS
CachedMethods:
- HEAD
- GET
ForwardedValues:
QueryString: true
Headers:
- Accept
- Referer
- Authorization
- Content-Type
ViewerProtocolPolicy: redirect-to-https
...
...Use this parameter if you want to add additional cache behaviors to the CloudFormation resources.
--no-delete-contents
optional, default false (deletes contents by default)
serverless client deploy --no-delete-contentsUse this parameter if you do not want to delete the contents of your bucket before deployment. Files uploaded during deployment will still replace any corresponding files already in your bucket.
--no-generate-client
optional, default false (generates client code by default if clientCommand and clientSrcPath are configured)
serverless client deploy --no-generate-clientUse this parameter if you do not want to generate the client code before deploying. Files uploaded during deployment will still replace any corresponding files already in your bucket.
--no-client-deploy
optional, default false (deploys the generated client code by default)
serverless deploy --no-client-deployUse this parameter if you do not want to deploy the client along with the rest of the serverless stack. Almost certainly in this case you don't want to generate the client code either and will want to use
serverless deploy --no-generate-client --no-client-deploy--no-confirm
optional, default false (disables confirmation prompt)
serverless client deploy --no-confirmUse this parameter if you do not want a confirmation prompt to interrupt automated builds.
--no-invalidate-distribution
optional, default false (disables creating an invalidation for the CloudFront distribution)
serverless client deploy --no-invalidate-distributionUse this parameter if you do not want to invalidate the CloudFront distribution. Invalidations are for the path /*.
This plugin has been hardened against common security vulnerabilities:
- Command Injection Protection - Spawn operations use
shell: falseto prevent command injection attacks - Path Traversal Protection - File operations validate paths to prevent directory traversal
- Secure Dependencies - All dependencies updated to latest secure versions:
js-yamlv4.1.0+ (fixes CVE-2019-20149 code execution vulnerability)lodashv4.17.21+ (fixes prototype pollution vulnerabilities)- All other dependencies updated to secure versions
- Use TLS 1.2 or higher - Set
minimumProtocolVersion: TLSv1.2_2021in your config - Enable WAF - Configure AWS WAF to protect against common web exploits
- Use SSL Certificates - Always use ACM certificates for custom domains
- Regular Updates - Keep the plugin and dependencies up to date
- Principle of Least Privilege - Use IAM roles with minimal required permissions
If you discover a security vulnerability, please email security reports to the maintainers or open a private security advisory on GitHub.
This plugin includes a comprehensive test suite with 96.8% code coverage.
# Install dependencies
npm install
# Run tests with coverage
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with verbose output
npm run test:verbose- Unit Tests: 151 tests across all modules
- Coverage: 96.8% overall
- index.js: 98.05%
- bucketUtils.js: 100%
- cloudFront.js: 100%
- validate.js: 100%
- upload.js: 82.22%
- getFileList.js: 93.33%
Contributions are welcome! Please ensure:
- All tests pass (
npm test) - Coverage remains above 95%
- Code follows existing style conventions
- Security best practices are followed
Security Fixes:
- π Fixed critical command injection vulnerability in spawn operations
- π Added path traversal protection to file operations
- π Updated
js-yamlfrom v3.10.0 to v4.1.0 (fixes CVE-2019-20149) - π Updated
lodashfrom v4.13.1 to v4.17.21 (fixes prototype pollution) - π Updated all dependencies to latest secure versions
Testing & Quality:
- β Added comprehensive test suite with 96.8% coverage
- β 151 unit tests across all modules
- β Added Jest testing framework with full mocking support
- β Configured coverage thresholds (95% minimum)
API Changes:
- Updated
yaml.safeLoad()toyaml.load()for js-yaml v4 compatibility
- Andy Hahn - andrewphahn from MadSkills.io
- jlaramie
- superandrew213
- harmon25
- jmortlock
- haochang
- hakimio
- artoliukkonen
- pecirep
- miguel-a-calles-mba
This plugin builds on the excellent work of:
- serverless-api-cloudfront - Original plugin inspiration
- serverless-finch - S3 deployment patterns
- Full Stack Serverless Web Apps with AWS - CloudFormation templates
- serverless-stack.com - Serverless best practices
MIT License - see LICENSE file for details