API Works in Postman but Fails from Angular — What Could Cause This?
This is a very common Angular + ASP.NET Core troubleshooting scenario.
The key clue is:
If the API works in Postman, but fails from Angular running in a browser, first investigate browser-specific behavior and differences between the Angular request and the Postman request.
The most common causes are:
CORS
Wrong API URL
HTTP/HTTPS mixed content
Missing JWT / Authorization header
CORS preflight OPTIONS failure
Different request headers
Different request body
Cookie / credentials configuration
SSL certificate problems
Angular interceptor issues
1. Why Can Postman Work While Angular Fails?
Postman and a browser are not the same environment.
For example:
Postman
|
| HTTP Request
v
ASP.NET Core API
|
└── Works
But Angular runs inside a browser:
Angular
|
v
Browser
|
| Browser security rules
| CORS
| Mixed-content rules
| Cookie rules
| Preflight OPTIONS
v
ASP.NET Core API
The browser imposes security restrictions that Postman generally does not.
Therefore:
Postman works
Angular fails
does not necessarily mean Angular code is wrong.
2. First Check — Browser Network Tab
My first troubleshooting step would be:
F12
↓
Network
↓
Trigger the Angular request
↓
Select failed request
Check:
- Request URL
- HTTP method
- Status code
- Request headers
- Response headers
- Request payload
- Response body
OPTIONSrequest, if present
Also check:
F12
↓
Console
The actual error usually tells you which direction to investigate.
3. Cause #1 — CORS
This is one of the most common reasons.
Suppose Angular runs at:
http://localhost:4200
and ASP.NET Core runs at:
https://localhost:7001
These are different origins.
The browser applies CORS rules.
Postman does not enforce browser CORS policy in the same way.
So:
Postman
↓
API
↓
Works
Angular
↓
Browser
↓
CORS check
↓
Blocked
4. ASP.NET Core CORS Configuration
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddCors(options =>
{
options.AddPolicy("AngularPolicy", policy =>
{
policy
.WithOrigins("http://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
var app = builder.Build();
app.UseHttpsRedirection();
app.UseCors("AngularPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Now ASP.NET Core explicitly allows requests from:
http://localhost:4200
5. Production CORS
Suppose production Angular is:
https://www.myapp.com
Then production CORS should allow that origin:
.WithOrigins(
"https://www.myapp.com"
)
Be careful:
https://myapp.com
and
https://www.myapp.com
are different origins.
Likewise:
http://myapp.com
and:
https://myapp.com
are different origins.
6. Cause #2 — Preflight OPTIONS Request Fails
Angular may send a request such as:
POST /api/products
Authorization: Bearer eyJ...
Content-Type: application/json
Before sending it, the browser may send:
OPTIONS /api/products
This is called a CORS preflight request.
Flow:
Angular
↓
Browser
↓
OPTIONS /api/products
↓
ASP.NET Core
↓
CORS response
↓
Browser permits actual request
↓
POST /api/products
If OPTIONS fails:
OPTIONS
↓
CORS / proxy / server rejects it
↓
Browser blocks request
↓
Actual POST may never be sent
Postman may appear to work because it doesn't perform browser CORS preflight enforcement.
How to check
Open:
F12 → Network
Look for:
OPTIONS
If the OPTIONS request fails, investigate CORS and web-server/proxy configuration.
7. Cause #3 — Angular Is Calling the Wrong API URL
Postman might call:
https://api.myapp.com/api/products
while Angular accidentally calls:
https://api.myapp.com/products
or even:
https://localhost:7001/api/products
Check the browser Network tab.
The Request URL should exactly match the expected API endpoint.
For Angular:
export const environment = {
apiUrl: 'https://api.myapp.com/api'
};
Then:
getProducts() {
return this.http.get(
`${environment.apiUrl}/products`
);
}
8. Cause #4 — Missing JWT Token
Postman may contain:
Authorization: Bearer eyJhbGciOi...
while Angular sends:
GET /api/products
without an Authorization header.
Then ASP.NET Core:
[Authorize]
returns:
401 Unauthorized
9. Check Angular Authorization Header
Open:
F12
↓
Network
↓
API Request
↓
Request Headers
You should see:
Authorization: Bearer eyJhbGciOi...
If not, investigate your Angular interceptor.
10. Angular JWT Interceptor
auth.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';
export const authInterceptor: HttpInterceptorFn =
(req, next) => {
const token =
localStorage.getItem('access_token');
if (!token) {
return next(req);
}
const authRequest = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next(authRequest);
};
Register it:
app.config.ts
import {
ApplicationConfig
} from '@angular/core';
import {
provideHttpClient,
withInterceptors
} from '@angular/common/http';
import {
authInterceptor
} from './auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([
authInterceptor
])
)
]
};
If you forget:
withInterceptors([
authInterceptor
])
the interceptor won't execute.
11. Cause #5 — HTTP/HTTPS Mixed Content
Suppose Angular is:
https://www.myapp.com
but API is:
http://api.myapp.com
The browser may block this.
HTTPS Angular
↓
HTTP API
↓
Mixed-content security restriction
↓
Blocked
Postman can still call:
http://api.myapp.com
because it isn't a web page operating under browser mixed-content rules.
Prefer:
https://www.myapp.com
↓
https://api.myapp.com
12. Cause #6 — SSL Certificate Problem
Postman and browsers can behave differently depending on certificate validation/settings.
Check:
https://api.myapp.com
directly in the browser.
Look for:
- expired certificate
- untrusted certificate
- hostname mismatch
- incomplete certificate chain
Production should use a valid trusted HTTPS certificate.
13. Cause #7 — Request Body Is Different
Postman may send:
{
"name": "Laptop",
"price": 50000
}
but Angular may accidentally send:
{
"productName": "Laptop",
"amount": 50000
}
ASP.NET Core expects:
public class ProductDto
{
public string Name { get; set; } = "";
public decimal Price { get; set; }
}
Depending on validation and model-binding configuration, the Angular request could result in:
400 Bad Request
Check
In:
F12 → Network → Payload
compare Angular's request payload with the working Postman request.
14. Cause #8 — Different Headers
Postman might send:
Content-Type: application/json
Authorization: Bearer ...
X-Api-Key: ABC123
Angular might send only:
Content-Type: application/json
If your ASP.NET Core API requires:
X-Api-Key
Angular must send it too.
For example:
const headers = {
'X-Api-Key': 'ABC123'
};
return this.http.get(url, {
headers
});
For sensitive production credentials, however, don't embed secret API keys in Angular bundles because browser-delivered code is visible to users.
15. Cause #9 — Cookie Authentication / Credentials
If authentication uses cookies across origins, Angular may need:
this.http.get(url, {
withCredentials: true
});
ASP.NET Core CORS then needs compatible credential configuration:
builder.Services.AddCors(options =>
{
options.AddPolicy("AngularPolicy", policy =>
{
policy
.WithOrigins(
"https://www.myapp.com"
)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
For credentialed CORS, don't combine:
.AllowAnyOrigin()
with:
.AllowCredentials()
Instead, specify the trusted origins explicitly.
Cookie settings such as SameSite and Secure may also matter for cross-site authentication.
16. Cause #10 — Angular Interceptor Changes the Request
Suppose you have multiple interceptors:
provideHttpClient(
withInterceptors([
authInterceptor,
loggingInterceptor,
errorInterceptor
])
)
An interceptor could accidentally:
- remove headers
- modify the URL
- modify the request body
- handle errors incorrectly
- attach an invalid/expired token
For example, check that your interceptor clones the request correctly:
const authRequest = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next(authRequest);
17. Cause #11 — Proxy Configuration
During Angular development you might use:
{
"/api": {
"target": "https://localhost:7001",
"secure": false
}
}
Then Angular calls:
this.http.get('/api/products');
During development:
Angular
localhost:4200
↓
Angular dev proxy
↓
localhost:7001
↓
API
But the Angular development proxy is not automatically your production proxy.
After:
ng build
the browser still needs a real production route to /api, usually provided by your production web server/reverse proxy, or Angular must use the deployed API URL.
This is a very common deployment mistake.
18. Compare Postman and Angular Side by Side
This is one of the best troubleshooting approaches.
| Check | Postman | Angular |
|---|---|---|
| URL | /api/products |
Same? |
| Method | GET/POST | Same? |
| Authorization | Bearer token | Same? |
| Content-Type | JSON | Same? |
| Custom headers | Present | Present? |
| Request body | Correct | Same? |
| Cookies | Present | Sent? |
| HTTPS | Works | Browser trusts it? |
| CORS | Not browser-enforced | Browser-enforced |
| Preflight | Not browser-enforced | May occur |
Don't just ask:
"Does Postman work?"
Ask:
"What is different between the successful Postman request and the failing browser request?"
That usually leads directly to the root cause.
19. Status Code Gives You a Clue
Use the actual error to narrow the investigation.
| Result | Likely area |
|---|---|
| CORS error | CORS configuration |
Failed OPTIONS |
Preflight/CORS/proxy |
400 |
Request payload/validation |
401 |
Authentication/token |
403 |
Authorization |
404 |
Wrong URL/routing |
405 |
HTTP method/proxy/preflight |
415 |
Content-Type |
500 |
API exception |
502 |
Gateway/reverse proxy |
| Certificate error | HTTPS/TLS |
| Mixed-content error | HTTPS frontend → HTTP API |
20. Full Simple Example
Assume:
Angular
http://localhost:4200
ASP.NET Core
https://localhost:7001
ASP.NET Core Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddCors(options =>
{
options.AddPolicy("Angular", policy =>
{
policy
.WithOrigins(
"http://localhost:4200"
)
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services
.AddAuthentication(
JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters =
new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "MyApi",
ValidAudience = "MyAngularApp",
IssuerSigningKey =
new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(
"ThisIsMySuperSecretKey123456789"
))
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseCors("Angular");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Angular auth.interceptor.ts
import {
HttpInterceptorFn
} from '@angular/common/http';
export const authInterceptor: HttpInterceptorFn =
(req, next) => {
const token =
localStorage.getItem('access_token');
if (!token) {
return next(req);
}
const authRequest = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next(authRequest);
};
Angular app.config.ts
import {
ApplicationConfig
} from '@angular/core';
import {
provideHttpClient,
withInterceptors
} from '@angular/common/http';
import {
authInterceptor
} from './auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([
authInterceptor
])
)
]
};
Angular product.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class ProductService {
private apiUrl =
'https://localhost:7001/api/products';
constructor(private http: HttpClient) {}
getProducts() {
return this.http.get(this.apiUrl);
}
}
The request becomes:
Angular ProductService
↓
authInterceptor
↓
Authorization: Bearer <JWT>
↓
Browser
↓
CORS / Preflight if required
↓
ASP.NET Core
↓
Authentication
↓
Authorization
↓
ProductsController
Troubleshooting Flow
A practical troubleshooting sequence is:
Postman works
Angular fails
↓
Open F12
↓
Network + Console
↓
Check exact API URL
↓
Check CORS error
↓
Check OPTIONS request
↓
Check HTTP status
↓
Check Authorization header
↓
Check request payload
↓
Check HTTPS / certificate
↓
Compare Angular request
with Postman request
Key Points
When an API works in Postman but fails from Angular, the strongest suspects are browser-specific restrictions or request differences.
Check these first:
- CORS
- Preflight
OPTIONS - Correct API URL
- Authorization/JWT header
- HTTPS mixed content
- SSL certificate
- Request body
- Content-Type/custom headers
- Cookies and
withCredentials - Angular interceptors
- Development proxy vs production routing
Most importantly:
Postman works
≠
Browser must work
Postman success tells you that the endpoint is reachable and can handle that Postman request. It does not prove that browser CORS, preflight, credentials, or Angular's actual request are correct.
Interview Questions and Answers
1. The API works in Postman but fails from Angular. What could cause this?
Interview Answer:
I would first inspect the browser Network and Console tabs. Common causes are CORS, a failed preflight request, an incorrect API URL, missing JWT headers, HTTPS mixed-content restrictions, certificate issues, or differences in headers and request payload between Angular and Postman.
2. Why does CORS affect Angular but not Postman?
Answer:
CORS is primarily a browser-enforced security mechanism for cross-origin web requests.
Angular runs inside the browser, while Postman is an API client and isn't subject to browser CORS enforcement in the same way.
3. What is a preflight request?
Answer:
A preflight is an HTTP:
OPTIONS
request that the browser may send before the actual cross-origin request to determine whether the server permits the requested method and headers.
4. Postman works but Angular returns 401. What would you check?
Answer:
I would compare the Authorization headers.
Angular should normally send:
Authorization: Bearer <JWT>
Then I would verify the Angular authentication interceptor and token validity.
5. Postman works but Angular shows a CORS error. Is the API broken?
Not necessarily.
The API may be functioning correctly, but it has not allowed the Angular application's origin under its CORS policy.
6. Why can Postman call an HTTP API while an HTTPS Angular application cannot?
The browser can block:
HTTPS Angular
↓
HTTP API
because it is mixed content.
Postman isn't operating as a script inside an HTTPS web page.
7. What is the best debugging technique in this situation?
Interview Answer:
I would compare the successful Postman request with the failing Angular request—URL, HTTP method, headers, authorization token, content type, cookies, and payload—and also inspect browser-specific issues such as CORS, preflight, and mixed content.
That is the core troubleshooting principle:
Successful Postman Request
↕
Compare
↕
Failing Angular Request
→ Find the difference
→ Find the root cause