Back to Checks
vercel

Vercel domains have a valid, non-expired SSL certificate

domain_ssl_certificate_valid
Service: domainSeverity: critical
OfficialBuilt-inVerified
Categories:encryptionvercel-hobby-plan
Resource Type:NotDefined

Vercel domains are assessed for SSL certificate validity including provisioning, expiration, and upcoming expiry. Vercel automatically provisions and renews SSL certificates for properly configured domains. A missing, expired, or soon-to-expire certificate indicates a configuration issue that may leave traffic unencrypted.

Risk

Without an SSL certificate, traffic between users and the domain is transmitted in plain text. This exposes sensitive data such as authentication tokens, form submissions, and personal information to interception via man-in-the-middle attacks. Search engines also penalize non-HTTPS sites, reducing visibility.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler vercel --checks domain_ssl_certificate_valid

Recommendation

Ensure domain DNS records are properly configured to point to Vercel. Once DNS is validated, Vercel automatically provisions and renews SSL/TLS certificates. Check the domain configuration in the Vercel dashboard if the certificate is not being issued.

Remediation

Other

1Sign in to the Vercel dashboard
2Navigate to the project Settings > Domains
3Verify the domain's DNS records point to Vercel correctly
4Vercel will automatically provision an SSL certificate once DNS is properly configured
5If issues persist, remove and re-add the domain

Check Code

from datetime import datetime, timezone
from typing import List

from prowler.lib.check.models import Check, CheckReportVercel, Severity
from prowler.providers.vercel.services.domain.domain_client import domain_client


class domain_ssl_certificate_valid(Check):
    """Check if domains have a valid, non-expired SSL certificate.

    This class verifies whether each Vercel domain has an SSL certificate
    that is provisioned, not expired, and not about to expire. The
    expiration threshold is configurable via ``days_to_expire_threshold``
    in audit_config (default: 7 days).
    """

    def execute(self) -> List[CheckReportVercel]:
        """Execute the Vercel Domain SSL Certificate check.

        Iterates over all domains and checks SSL certificate presence and
        expiration status.

        Returns:
            List[CheckReportVercel]: A list of reports for each domain.
        """
        findings = []
        now = datetime.now(timezone.utc)
        days_to_expire_threshold = domain_client.audit_config.get(
            "days_to_expire_threshold", 7
        )

        for domain in domain_client.domains.values():
            report = CheckReportVercel(
                metadata=self.metadata(),
                resource=domain,
                resource_name=domain.name,
                resource_id=domain.id or domain.name,
            )

            if domain.ssl_certificate is None:
                report.status = "FAIL"
                report.check_metadata.Severity = Severity.high
                report.status_extended = f"Domain {domain.name} does not have an SSL certificate provisioned."
            elif (
                domain.ssl_certificate.expires_at is not None
                and domain.ssl_certificate.expires_at <= now
            ):
                report.status = "FAIL"
                report.check_metadata.Severity = Severity.critical
                report.status_extended = (
                    f"Domain {domain.name} has an SSL certificate that expired "
                    f"on {domain.ssl_certificate.expires_at.strftime('%Y-%m-%d %H:%M UTC')}."
                )
            elif domain.ssl_certificate.expires_at is not None:
                days_left = (domain.ssl_certificate.expires_at - now).days
                if days_left <= days_to_expire_threshold:
                    report.status = "FAIL"
                    report.check_metadata.Severity = Severity.high
                    report.status_extended = (
                        f"Domain {domain.name} has an SSL certificate expiring "
                        f"in {days_left} days "
                        f"on {domain.ssl_certificate.expires_at.strftime('%Y-%m-%d %H:%M UTC')}."
                    )
                else:
                    report.status = "PASS"
                    report.status_extended = (
                        f"Domain {domain.name} has a valid SSL certificate expiring "
                        f"on {domain.ssl_certificate.expires_at.strftime('%Y-%m-%d %H:%M UTC')}."
                    )
            else:
                report.status = "PASS"
                report.status_extended = (
                    f"Domain {domain.name} has an SSL certificate provisioned."
                )

            findings.append(report)

        return findings