This check flags GCP Cloud Functions whose HTTP trigger accepts plaintext HTTP instead of forcing HTTPS, leaving request data exposed to interception. Fix it by setting --security-level=secure-always on the function so all traffic is redirected to HTTPS.
HTTP triggers are the simplest way to expose a Cloud Function to the outside world. Send a request, get a response, no Pub/Sub or event plumbing required. The catch is that an HTTP trigger can be configured to accept plaintext HTTP connections, and when it does, everything in that request travels across the network unencrypted.
The functions_nohttps check catches exactly this: a Cloud Function whose HTTP trigger does not enforce HTTPS. It is one of the cheaper misconfigurations to fix and one of the more dangerous to ignore.
What this check detects
Every Gen 1 Cloud Function with an HTTP trigger has a security setting that controls which protocols it will answer to. That setting has three possible values:
- SECURE_ALWAYS — only HTTPS is accepted. Plaintext HTTP requests are redirected to HTTPS.
- SECURE_OPTIONAL — both HTTP and HTTPS are accepted, with no redirect.
- Unset — behaves like optional in older configurations, accepting plaintext.
The check fails when a function is configured with SECURE_OPTIONAL or has no security level set, meaning it will happily respond to plaintext http:// requests.
Note: This setting (httpsTrigger.securityLevel) applies to Gen 1 Cloud Functions. Gen 2 Cloud Functions run on Cloud Run under the hood and enforce HTTPS at the ingress layer by default, so the controls differ. If you manage a mixed estate, check the generation of each function before applying fixes.
Why it matters
An HTTP trigger that accepts plaintext is an open invitation for traffic interception. When a client calls your function over http://, the entire exchange is readable to anyone positioned on the network path: corporate proxies, compromised Wi-Fi access points, malicious routers, or an attacker doing ARP spoofing on a shared network.
Here is what that exposes in practice:
- Credentials and tokens. API keys, bearer tokens, and session identifiers passed in headers or query strings are sent in the clear.
- Request and response bodies. Anything the function processes, from form data to JSON payloads, is visible on the wire.
- Manipulation, not just observation. Without TLS there is no integrity guarantee. An attacker in the middle can modify the request or the response, injecting malicious data or altering function output.
A common failure pattern looks like this: a function is deployed for internal testing with HTTP allowed, then quietly graduates to handling real traffic. The webhook URL gets shared, a frontend starts calling it, and now production secrets are crossing the network unencrypted because nobody flipped the security setting back.
Warning: Even if your callers always type https://, an open HTTP listener is still a downgrade risk. An attacker who can influence DNS or routing can strip the scheme and force the victim onto plaintext. Enforcing HTTPS at the function removes that option entirely.
For anyone subject to PCI DSS, HIPAA, or SOC 2, transmitting sensitive data over unencrypted HTTP is also a straightforward compliance failure. Auditors look for encryption in transit, and a function answering on plain HTTP is an easy finding.
How to fix it
The fix is to set the security level to secure-always. This is a configuration change, not a redeploy of your code, and it takes effect quickly.
Using the gcloud CLI
First, confirm the current security level of the function:
gcloud functions describe my-function \
--region=us-central1 \
--format="value(httpsTrigger.securityLevel)"
If that returns SECURE_OPTIONAL or nothing, update it:
gcloud functions deploy my-function \
--region=us-central1 \
--security-level=secure-always
Warning: If any current caller is hardcoded to use http:// without following redirects, it will break after this change. Audit your clients first. Most HTTP libraries follow the 301 redirect to HTTPS automatically, but webhook senders and minimal scripts sometimes do not.
Using Terraform
For the google_cloudfunctions_function resource, set the security level inside the trigger block:
resource "google_cloudfunctions_function" "my_function" {
name = "my-function"
runtime = "nodejs20"
region = "us-central1"
entry_point = "handler"
trigger_http = true
https_trigger_security_level = "SECURE_ALWAYS"
source_archive_bucket = google_storage_bucket.source.name
source_archive_object = google_storage_bucket_object.archive.name
}
Apply it:
terraform plan
terraform apply
Verify the fix
Confirm that plaintext requests are now redirected. A curl against the HTTP URL should return a redirect to HTTPS rather than a 200:
curl -sI "http://my-function-url.cloudfunctions.net/my-function" | head -n 1
# Expect: HTTP/1.1 301 Moved Permanently (or similar redirect)
Tip: Loop over all functions in a region to find which ones still allow plaintext, instead of checking them one at a time:
for fn in $(gcloud functions list --regions=us-central1 --format="value(name)"); do
level=$(gcloud functions describe "$fn" --region=us-central1 --format="value(httpsTrigger.securityLevel)")
echo "$fn: ${level:-UNSET}"
done
How to prevent it from happening again
Fixing one function is easy. Stopping the next one from shipping with plaintext enabled is the real goal. Push the control left so it never reaches production.
1. Make secure-always the default in your modules
If teams provision functions through a shared Terraform module, bake the secure value in and do not expose an override. A function that cannot be created with plaintext enabled is a function you never have to remediate.
variable "https_security_level" {
type = string
default = "SECURE_ALWAYS"
validation {
condition = var.https_security_level == "SECURE_ALWAYS"
error_message = "HTTP triggers must enforce HTTPS. SECURE_OPTIONAL is not permitted."
}
}
2. Enforce it as policy with OPA or Conftest
Run a policy-as-code gate against your Terraform plan in CI so a non-compliant function fails the build:
package functions.https
deny[msg] {
resource := input.resource_changes[_]
resource.type == "google_cloudfunctions_function"
level := resource.change.after.https_trigger_security_level
level != "SECURE_ALWAYS"
msg := sprintf("Function '%s' must set https_trigger_security_level to SECURE_ALWAYS", [resource.name])
}
Wire it into your pipeline against a plan in JSON:
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json
conftest test tfplan.json --policy policy/
3. Catch drift in the running environment
Policy gates only cover resources that go through your pipeline. Someone with console access can still flip a setting manually, or an older function may predate your policy. Continuous scanning closes that gap by checking deployed functions on a schedule, which is exactly what the Lensix functions_nohttps check does across your whole GCP estate.
Tip: Pair the build-time gate with continuous scanning. The CI check stops new misconfigurations at the door, and the runtime scan catches anything created outside the pipeline or changed by hand afterward.
Best practices
- Always enforce HTTPS on HTTP triggers. There is no good reason to accept plaintext in production. Make
SECURE_ALWAYSthe only acceptable value. - Do not rely on the trigger for authentication. HTTPS protects data in transit, but it does not decide who may call the function. Restrict invocation with IAM by removing
allUsersfrom theroles/cloudfunctions.invokerbinding and granting it only to the identities that need it. - Keep secrets out of URLs. Query strings end up in logs and proxy caches even over HTTPS. Pass tokens in headers and store sensitive values in Secret Manager rather than environment variables where practical.
- Prefer Gen 2 for new work. Gen 2 functions inherit Cloud Run ingress controls and enforce HTTPS by default, which removes this class of misconfiguration for new deployments.
- Audit on a schedule, not once. Configuration drifts. A function that is compliant today can be changed tomorrow, so treat this as a recurring check rather than a one-time cleanup.
Enforcing HTTPS on a Cloud Function is a one-line change with an outsized security payoff. Set it on every HTTP trigger, gate it in CI, and scan continuously so the one function someone spins up at 5pm on a Friday does not become the gap an attacker walks through.

