Symptoms
AWS Security Groups act as virtual firewalls for EC2 instances and other VPC resources, controlling inbound and outbound traffic at the elastic network interface (ENI) level. When a Security Group is misconfigured, the result is almost always a silent drop — packets disappear without an ICMP error or TCP RST, leaving engineers staring at connection timeouts rather than actionable error messages.
Common symptoms of an AWS Security Group blocking traffic include:
- SSH or RDP sessions hanging indefinitely with no prompt or error
curl
orwget
commands timing out with no response body- Application Load Balancer targets marked as unhealthy despite the application running correctly on the instance
- Database connections from application servers failing with a timeout (not a refused connection)
- ICMP ping returning no response when testing basic connectivity
- VPC Flow Logs showing
REJECT
action for otherwise expected traffic flows - AWS Systems Manager Session Manager succeeding while direct TCP connections on custom ports fail, confirming the instance is running but a port is blocked
The critical diagnostic distinction: Connection refused means the packet reached the host but no service is listening on that port. Connection timeout means the packet never reached the host, or the return path is blocked. Security Group issues almost always produce timeouts, never refused errors.
Root Cause 1: Wrong Port in Inbound Rule
Why It Happens
This is the single most common Security Group mistake. Engineers remember to open a port but enter the wrong number — for example, opening TCP 8080 when the application actually listens on 8443, or opening port 22 when SSH was moved to port 2222 for hardening purposes. Numeric confusion between similar port numbers is also frequent: 3306 versus 33060 for MySQL versus MySQL X Protocol, or 5432 versus 54320 for PostgreSQL versus a custom service. Even off-by-one errors (8080 vs 8081) are enough to cause complete failure.
How to Identify It
First, confirm the port the process is actually listening on. Use AWS Systems Manager Session Manager to access the instance without needing SSH access through the Security Group:
$ ss -tlnp | grep LISTEN
LISTEN 0 128 0.0.0.0:2222 0.0.0.0:* users:(("sshd",pid=1234,fd=3))
LISTEN 0 128 0.0.0.0:8443 0.0.0.0:* users:(("java",pid=5678,fd=12))
Then describe the Security Group inbound rules using the AWS CLI:
$ aws ec2 describe-security-groups \
--group-ids sg-0abc123def456789 \
--query 'SecurityGroups[*].IpPermissions' \
--output table
------------------------------------------------------
| DescribeSecurityGroups |
+----------+-----------+----------+-----------------+
| FromPort | IpProtocol| ToPort | CidrIp |
+----------+-----------+----------+-----------------+
| 22 | tcp | 22 | 10.0.0.0/16 |
| 8080 | tcp | 8080 | 10.0.0.0/16 |
+----------+-----------+----------+-----------------+
The Security Group allows port 8080, but the application listens on 8443. All traffic to that service is silently dropped.
How to Fix It
Add the correct inbound rule and remove the incorrect one:
# Add the correct rule
$ aws ec2 authorize-security-group-ingress \
--group-id sg-0abc123def456789 \
--protocol tcp \
--port 8443 \
--cidr 10.0.0.0/16
# Remove the wrong rule
$ aws ec2 revoke-security-group-ingress \
--group-id sg-0abc123def456789 \
--protocol tcp \
--port 8080 \
--cidr 10.0.0.0/16
Verify the corrected rules:
$ aws ec2 describe-security-groups \
--group-ids sg-0abc123def456789 \
--query 'SecurityGroups[0].IpPermissions[*].[FromPort,ToPort,IpProtocol,IpRanges[0].CidrIp]' \
--output table
-------------------------------------
| 22 | 22 | tcp | 10.0.0.0/16 |
| 8443 | 8443 | tcp | 10.0.0.0/16 |
-------------------------------------
Root Cause 2: Wrong CIDR Block
Why It Happens
A Security Group rule may allow the correct port but scope it to the wrong IP range. This happens when the source IP is misidentified — for example, allowing
10.0.1.0/24when traffic arrives from
10.0.2.50in a different subnet. It also occurs when an on-premises IP range changes and the Security Group is never updated, or when engineers copy CIDR blocks from documentation without validating them against the actual network topology. Subnet mask mistakes (using /32 for a host when intending /24 for a subnet) are another frequent variant.
How to Identify It
Identify the actual source IP of the failing connection using VPC Flow Logs:
$ aws logs filter-log-events \
--log-group-name /aws/vpc/flowlogs \
--filter-pattern "[version, account, eni, srcaddr=10.0.2.*, dstaddr=10.0.1.50, srcport, dstport=443, protocol=6, packets, bytes, start, end, action=REJECT, status]" \
--start-time $(date -d '30 minutes ago' +%s000)
{
"srcaddr": "10.0.2.100",
"dstaddr": "10.0.1.50",
"dstport": "443",
"action": "REJECT"
}
Compare the actual source IP against the Security Group inbound rules:
$ aws ec2 describe-security-groups \
--group-ids sg-0abc123def456789 \
--query 'SecurityGroups[0].IpPermissions[?FromPort==`443`]'
[
{
"FromPort": 443,
"IpProtocol": "tcp",
"IpRanges": [
{
"CidrIp": "10.0.1.0/24"
}
],
"ToPort": 443
}
]
The rule allows only
10.0.1.0/24, but traffic arrives from
10.0.2.100— outside the permitted CIDR. The Security Group drops it.
How to Fix It
Update the CIDR to cover the correct source range. In this case, the full VPC CIDR
10.0.0.0/16covers all internal subnets:
# Add the correct broader CIDR
$ aws ec2 authorize-security-group-ingress \
--group-id sg-0abc123def456789 \
--protocol tcp \
--port 443 \
--cidr 10.0.0.0/16
# Remove the overly narrow CIDR
$ aws ec2 revoke-security-group-ingress \
--group-id sg-0abc123def456789 \
--protocol tcp \
--port 443 \
--cidr 10.0.1.0/24
Root Cause 3: Missing Outbound Rule
Why It Happens
By default, AWS Security Groups allow all outbound traffic (
0.0.0.0/0on all ports and protocols). However, teams that harden their Security Groups by removing this default all-allow egress rule must explicitly define every outbound rule for every destination the instance needs to reach. When a required outbound rule is missing, the instance initiates a connection, but the SYN packet is dropped before it ever leaves the ENI. This is particularly common on EC2 instances acting as clients: worker nodes polling SQS queues, application servers connecting to RDS, and Lambda functions inside a VPC calling external API endpoints.
How to Identify It
Inspect the egress rules on the Security Group:
$ aws ec2 describe-security-groups \
--group-ids sg-0abc123def456789 \
--query 'SecurityGroups[*].IpPermissionsEgress' \
--output json
[
[
{
"IpProtocol": "tcp",
"FromPort": 443,
"ToPort": 443,
"IpRanges": [
{
"CidrIp": "10.0.3.0/24"
}
]
}
]
]
The instance needs to reach a PostgreSQL database on port 5432 at
10.0.4.20, but only outbound TCP 443 is permitted. Confirm the block in VPC Flow Logs:
$ aws logs filter-log-events \
--log-group-name /aws/vpc/flowlogs \
--filter-pattern "[version, account, eni, srcaddr=10.0.1.50, dstaddr=10.0.4.20, srcport, dstport=5432, protocol=6, packets, bytes, start, end, action=REJECT, status]"
{
"srcaddr": "10.0.1.50",
"dstaddr": "10.0.4.20",
"dstport": "5432",
"action": "REJECT"
}
How to Fix It
Add the required outbound rule for PostgreSQL:
$ aws ec2 authorize-security-group-egress \
--group-id sg-0abc123def456789 \
--protocol tcp \
--port 5432 \
--cidr 10.0.4.0/24
If the instance requires broad outbound access and the security posture permits it, restore the default allow-all egress rule:
$ aws ec2 authorize-security-group-egress \
--group-id sg-0abc123def456789 \
--ip-permissions '[{"IpProtocol": "-1", "IpRanges": [{"CidrIp": "0.0.0.0/0"}]}]'
Root Cause 4: Stateful Rule Misunderstanding
Why It Happens
AWS Security Groups are stateful. When an inbound rule permits a connection, the return traffic for that session is automatically allowed — even with no matching outbound rule for the return path. Engineers who have worked extensively with stateless firewalls (raw iptables, pfSense without state tracking, or AWS Network ACLs) frequently misapply stateless mental models to Security Groups.
This creates two failure patterns:
- Unnecessary bidirectional rules: An engineer opens port 443 inbound and also adds a matching outbound rule for port 443, believing both are needed. They later delete the outbound rule during a cleanup audit, nothing breaks (because return traffic is stateful), and they incorrectly conclude outbound rules are never needed. They then fail to add a required outbound rule when the instance acts as a client, and the connection times out.
- Missing client-side outbound rules: An engineer only adds inbound rules for every service, not realizing that when this instance is the client initiating a connection to another host, an outbound rule is absolutely required. The return traffic to the ephemeral port is stateful and auto-permitted — but the initial outbound SYN is not.
How to Identify It
Determine whether the instance is acting as a server (accepting connections) or a client (initiating connections) for the failing traffic:
# Connect via SSM Session Manager and run:
$ ss -tnp | grep ESTABLISHED
# Server mode (incoming connection — remote is the source):
ESTAB 0 0 10.0.1.50:443 10.0.2.100:54321 users:(("nginx",pid=902,fd=8))
# Client mode (outgoing connection — local has ephemeral port):
ESTAB 0 0 10.0.1.50:49823 10.0.4.20:5432 users:(("app",pid=3344,fd=11))
For client-mode traffic, check whether the relevant outbound rule exists:
$ aws ec2 describe-security-groups \
--group-ids sg-0abc123def456789 \
--query 'SecurityGroups[0].IpPermissionsEgress[?FromPort==`5432`]'
[] # Empty array — no outbound rule exists for port 5432
How to Fix It
For server-mode instances that only receive inbound connections: only an inbound rule is needed. The stateful engine automatically permits return traffic. Do not add outbound rules for the same port to match inbound rules — they are redundant.
For client-mode instances that initiate outbound connections: an outbound rule is required for the destination port. The return traffic arriving on an ephemeral source port is automatically permitted by the stateful engine — you do not need an inbound rule for ephemeral ports (1024–65535) on the client Security Group.
# Client initiating PostgreSQL connections: add outbound rule ONLY
$ aws ec2 authorize-security-group-egress \
--group-id sg-0abc123def456789 \
--protocol tcp \
--port 5432 \
--cidr 10.0.4.0/24
# DO NOT add: inbound allow for ephemeral ports 1024-65535
# The stateful connection tracker handles return traffic automatically.
Root Cause 5: NACL Overriding the Security Group
Why It Happens
Network Access Control Lists (NACLs) operate at the subnet level and are evaluated independently of Security Groups. For inbound traffic, the NACL is evaluated first, before the Security Group. For outbound traffic, the Security Group is evaluated first, then the NACL. Critically, unlike Security Groups, NACLs are stateless — each direction of every flow must be explicitly permitted, including the return path using ephemeral ports.
When a NACL has an explicit DENY rule — or simply lacks a required ALLOW rule — it blocks traffic that the Security Group would otherwise permit. Engineers debugging Security Groups often forget NACLs exist entirely, especially because the default NACL for a VPC allows all traffic and most teams never modify it. The problem arises when a custom NACL is created and associated with a subnet. Custom NACLs deny all traffic by default (via the implicit rule 32767 DENY), and any traffic not explicitly permitted is silently dropped regardless of the Security Group configuration.
How to Identify It
Find the subnet of the affected instance and locate its associated NACL:
# Step 1: Get the subnet ID of the instance
$ aws ec2 describe-instances \
--instance-ids i-0abc123def456789 \
--query 'Reservations[0].Instances[0].SubnetId' \
--output text
subnet-0def456abc789012
# Step 2: Find the NACL associated with that subnet
$ aws ec2 describe-network-acls \
--filters "Name=association.subnet-id,Values=subnet-0def456abc789012" \
--query 'NetworkAcls[0].{NaclId:NetworkAclId,Entries:Entries}' \
--output json
{
"NaclId": "acl-0abc123456def789",
"Entries": [
{
"CidrBlock": "10.0.0.0/16",
"Egress": false,
"Protocol": "6",
"RuleAction": "allow",
"RuleNumber": 100,
"PortRange": { "From": 443, "To": 443 }
},
{
"CidrBlock": "0.0.0.0/0",
"Egress": false,
"Protocol": "-1",
"RuleAction": "deny",
"RuleNumber": 32767
}
]
}
Inbound port 8443 has no ALLOW entry — the implicit DENY at rule 32767 drops it. Now check the egress entries — NACLs are stateless, so return traffic on ephemeral ports must also be explicitly permitted outbound:
$ aws ec2 describe-network-acls \
--filters "Name=association.subnet-id,Values=subnet-0def456abc789012" \
--query 'NetworkAcls[0].Entries[?Egress==`true`]' \
--output json
[
{
"CidrBlock": "0.0.0.0/0",
"Egress": true,
"Protocol": "-1",
"RuleAction": "deny",
"RuleNumber": 32767
}
]
There are zero egress ALLOW rules. All return traffic is blocked at the NACL level, regardless of what the Security Group permits.
How to Fix It
Add inbound and outbound NACL entries. Because NACLs are stateless, rules for both directions are mandatory:
# Allow inbound port 8443 from VPC CIDR
$ aws ec2 create-network-acl-entry \
--network-acl-id acl-0abc123456def789 \
--rule-number 110 \
--protocol tcp \
--rule-action allow \
--ingress \
--cidr-block 10.0.0.0/16 \
--port-range From=8443,To=8443
# Allow outbound ephemeral return traffic to VPC CIDR
$ aws ec2 create-network-acl-entry \
--network-acl-id acl-0abc123456def789 \
--rule-number 100 \
--protocol tcp \
--rule-action allow \
--egress \
--cidr-block 10.0.0.0/16 \
--port-range From=1024,To=65535
Important: NACL rules are evaluated in ascending order by rule number. A DENY at a lower rule number overrides an ALLOW at a higher rule number for matching traffic. Always verify no explicit DENY rule at a lower number precedes your new ALLOW entry for the same traffic class.
Root Cause 6: Wrong Protocol Specified
Why It Happens
AWS Security Group rules are protocol-specific. A rule opening port 53 for TCP will not allow DNS queries that use UDP port 53. A rule permitting TCP does not permit ICMP ping. Engineers frequently assume a port-based rule covers all protocols on that port, or they forget that certain services like DNS operate on both TCP and UDP. Rules allowing only ICMP type 8 (echo request) but not type 0 (echo reply) cause one-way ping failures that look like intermittent connectivity issues.
How to Identify It
$ aws ec2 describe-security-groups \
--group-ids sg-0abc123def456789 \
--query 'SecurityGroups[0].IpPermissions[?FromPort==`53`]'
[
{
"FromPort": 53,
"IpProtocol": "tcp",
"IpRanges": [{"CidrIp": "10.0.0.0/16"}],
"ToPort": 53
}
]
Only TCP port 53 is permitted. Route 53 Resolver and most DNS clients default to UDP port 53. Validate from the instance:
$ dig @10.0.0.2 solvethenetwork.com +tcp # Succeeds (TCP allowed)
$ dig @10.0.0.2 solvethenetwork.com # Times out (UDP blocked)
How to Fix It
$ aws ec2 authorize-security-group-egress \
--group-id sg-0abc123def456789 \
--protocol udp \
--port 53 \
--cidr 10.0.0.0/16
Root Cause 7: Security Group Not Attached to the Instance
Why It Happens
Engineers sometimes create and correctly configure a Security Group, then forget to attach it to the target instance or network interface. The instance continues using its original Security Group, which has no relevant rules for the new service. This is common in Terraform or CloudFormation deployments where the Security Group resource is correctly defined but the
vpc_security_group_idsor
SecurityGroupIdsreference is missing from the instance or launch template configuration.
How to Identify It
$ aws ec2 describe-instances \
--instance-ids i-0abc123def456789 \
--query 'Reservations[0].Instances[0].SecurityGroups'
[
{
"GroupId": "sg-0old111111111111",
"GroupName": "default"
}
]
The newly created Security Group
sg-0abc123def456789is not present. The instance only has the default SG, which has no relevant rules.
How to Fix It
Attach the correct Security Group to the instance. For a primary network interface, modify the instance attribute directly (this replaces all attached SGs, so include any you want to keep):
# Retrieve the network interface ID first
$ aws ec2 describe-instances \
--instance-ids i-0abc123def456789 \
--query 'Reservations[0].Instances[0].NetworkInterfaces[0].NetworkInterfaceId' \
--output text
eni-0abc123def456789
# Modify the network interface to attach both SGs
$ aws ec2 modify-network-interface-attribute \
--network-interface-id eni-0abc123def456789 \
--groups sg-0abc123def456789 sg-0old111111111111
Root Cause 8: Security Group Rule Limit Exceeded
Why It Happens
AWS imposes a default quota of 60 inbound rules and 60 outbound rules per Security Group. When a Security Group reaches this limit, new
authorize-security-group-ingressor
authorize-security-group-egresscalls fail with a hard error. Security Groups accumulate rules over time as infrastructure grows — temporary access rules added for debugging, IP ranges that are no longer valid, and overlapping rules from multiple teams all consume quota. When the quota is exhausted, legitimate required rules simply cannot be added.
How to Identify It
# Count current inbound rules
$ aws ec2 describe-security-groups \
--group-ids sg-0abc123def456789 \
--query 'length(SecurityGroups[0].IpPermissions)'
60
# Attempt to add a rule — the error:
An error occurred (RulesPerSecurityGroupLimitExceeded) when calling the
AuthorizeSecurityGroupIngress operation: The maximum number of rules per
security group has been reached.
How to Fix It
Audit and remove stale or redundant rules. You can also consolidate multiple individual /32 host entries into a single CIDR, or request a service quota increase:
# Request a quota increase for inbound rules per SG
$ aws service-quotas request-service-quota-increase \
--service-code ec2 \
--quota-code L-0EA8095F \
--desired-value 120
# Check current quota values
$ aws service-quotas get-service-quota \
--service-code ec2 \
--quota-code L-0EA8095F
Prevention
Preventing Security Group misconfigurations is substantially easier than debugging them in production. Implement these practices across your AWS environment:
- Enable VPC Flow Logs on all VPCs from day one. Flow logs are the most effective diagnostic tool for Security Group and NACL issues. Publish them to CloudWatch Logs Insights or S3, and retain them for at least 30 days. Without flow logs, debugging network drops becomes a process of elimination rather than evidence-based investigation.
- Use AWS Network Reachability Analyzer before and after changes. This tool analyzes whether a specific source-destination pair can communicate and identifies exactly which Security Group rule or NACL entry is blocking the path — without transmitting any actual test traffic. Run it proactively after any Security Group change to validate intent.
- Reference Security Groups instead of CIDR ranges wherever possible. When the traffic source is another EC2 instance, ECS task, or RDS cluster, reference its Security Group ID as the inbound source rather than its IP address. Security Group references automatically track IP changes and eliminate CIDR mismatch errors entirely.
- Enforce Infrastructure as Code for all Security Group changes. Terraform, CDK, and CloudFormation enforce code review for Security Group mutations, which catches port and CIDR errors before they ever reach production. Direct console edits should be treated as break-glass exceptions, not standard workflow.
- Standardize naming and tagging. Include the service name, environment tier, and owning team in every Security Group name. Use the Description field to document the intended purpose. Tags should include the team, cost center, and creation date for audit traceability.
- Never modify or attach resources to the default Security Group. Leave the default VPC Security Group with no inbound or outbound rules and keep it unattached to any resource. It exists only as a fallback and its unrestricted default rules are a compliance liability.
- Audit Security Groups regularly with AWS Config managed rules. Enable
vpc-sg-open-only-to-authorized-ports
,restricted-ssh
, andrestricted-common-ports
to detect overly permissive rules automatically and generate findings before they cause a security incident. - Document the stateful (SG) vs. stateless (NACL) distinction in all onboarding runbooks. This misunderstanding is a recurring source of production misconfigurations. New engineers working on AWS networking should understand the distinction on day one, not after their first production incident.
Frequently Asked Questions
Q: How do I tell if a Security Group is blocking traffic versus the application not listening on the port?
A: The connection behavior is the key indicator. A connection timeout (the connection hangs with no response for the full TCP timeout period) means the packet was dropped before reaching the OS — this points to a Security Group, NACL, or routing issue. A connection refused error (immediate response with RST) means the packet reached the host but no process is listening on that port. Use
nc -zv 10.0.1.50 8443from the source to observe the exact behavior. A timeout means Security Group; a refused means application.
Q: Do Security Group rules apply to traffic between instances in the same VPC?
A: Yes. Security Groups evaluate all traffic arriving at an ENI, regardless of origin. Traffic between two EC2 instances in the same VPC subnet is still subject to the destination instance's inbound Security Group rules. Same-subnet traffic is not implicitly trusted or bypassed.
Q: Can I attach multiple Security Groups to one EC2 instance, and how are they evaluated?
A: Yes, up to 5 Security Groups can be attached per network interface (adjustable via service quota). AWS evaluates all attached Security Groups as a union. If any single Security Group has an inbound rule that permits the traffic, it is allowed. There is no priority ordering, no most-specific-wins logic, and no deny rules within Security Groups — only allows. The only implicit deny is for traffic not matched by any rule across any attached Security Group.
Q: Do Security Group rule changes take effect immediately?
A: Yes. Security Group rule changes propagate and take effect within seconds for all traffic — including existing established connections. You do not need to restart instances, reboot, or terminate connections for new rules to apply.
Q: What is the functional difference between a Security Group and a Network ACL?
A: Security Groups are stateful, operate at the ENI (instance) level, support only ALLOW rules, and evaluate all attached SGs as a union. Network ACLs are stateless, operate at the subnet level, support both ALLOW and DENY rules, and evaluate rules in ascending rule number order with the first match winning. NACLs are evaluated before Security Groups for inbound traffic. Both must permit traffic for it to flow.
Q: Why does AWS Network Reachability Analyzer show a path as reachable, but traffic is still failing?
A: Reachability Analyzer tests the logical rule configuration only — it does not send actual data plane traffic. It will not detect OS-level firewalls (iptables, nftables, Windows Firewall), application-layer ACLs, routing table issues inside the guest OS, or network interfaces in unexpected states. If Reachability Analyzer shows the path as clear but traffic still fails, move your investigation to the instance OS, the application configuration, and the guest routing table.
Q: Can a Security Group block traffic between instances that share the same Security Group?
A: Yes. By default, instances sharing the same Security Group cannot communicate with each other unless an explicit inbound rule is added with the Security Group itself as the source (a self-referencing rule). To allow all instances in Security Group
sg-0abc123def456789to communicate with each other on all ports, add an inbound rule with source set to
sg-0abc123def456789and protocol set to
-1(all traffic).
Q: What happens if I try to delete a Security Group that is still attached to a running instance?
A: AWS will reject the deletion with the error:
DependencyViolation: resource sg-0abc123def456789 has a dependent object. You must first detach the Security Group by modifying the instance or network interface to use a different Security Group, then delete it. The same applies to Security Groups referenced as sources or destinations in other Security Groups' rules — those references must be removed before deletion.
Q: Does AWS Systems Manager Session Manager bypass Security Group restrictions?
A: Session Manager communicates over HTTPS (port 443) to the SSM VPC endpoints or the public SSM service endpoints — it does not use SSH port 22. If the instance Security Group blocks outbound TCP 443, Session Manager will fail. If your VPC has no internet gateway and no VPC endpoints for SSM (
ssm,
ssmmessages,
ec2messages), Session Manager will also fail regardless of Security Group rules. Session Manager is extremely useful for debugging Security Groups precisely because it uses a different path than SSH.
Q: How do I find all Security Groups in my account that have no rules and are not attached to any resource?
A: Use this CLI query to find unused Security Groups. First list all SG IDs in use by EC2 network interfaces, then compare against all Security Groups in the account:
# List all SG IDs currently attached to network interfaces
$ aws ec2 describe-network-interfaces \
--query 'NetworkInterfaces[*].Groups[*].GroupId' \
--output text | tr '\t' '\n' | sort -u > /tmp/used_sgs.txt
# List all SG IDs in the account
$ aws ec2 describe-security-groups \
--query 'SecurityGroups[*].GroupId' \
--output text | tr '\t' '\n' | sort -u > /tmp/all_sgs.txt
# Find orphaned SGs
$ comm -23 /tmp/all_sgs.txt /tmp/used_sgs.txt
Q: Is there a way to test Security Group rules without making live traffic changes?
A: Yes. Use AWS Network Reachability Analyzer to validate rule logic without sending traffic. Additionally, the AWS Console Security Group rule evaluation view shows which rule would match a given source IP, port, and protocol combination. For more comprehensive testing, AWS Inspector and third-party tools like Steampipe can query Security Group configurations and flag rules that deviate from expected policies — all without touching live traffic flows.
Q: Why do I see ACCEPT entries in VPC Flow Logs but the application still reports connection failures?
A: ACCEPT in VPC Flow Logs means the packet was permitted by the Security Group and NACL — it does not mean the full TCP handshake completed successfully or that the application processed the request. If you see ACCEPT logs for SYN packets but no corresponding ACCEPT for the return SYN-ACK, the return path is blocked (check the destination instance's outbound rules or the NACL egress rules on the source subnet). If both directions show ACCEPT but the application fails, the issue is above Layer 4: TLS certificate errors, application crashes, or resource exhaustion on the server.
