Skip to content

Angular 19 AngularAppEngine angular-app-engine-manifest.mjs needs to be edited when build with --base-href #30082

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 task
regiss opened this issue Apr 9, 2025 · 12 comments
Labels
area: @angular/ssr needs: more info Reporter must clarify the issue

Comments

@regiss
Copy link

regiss commented Apr 9, 2025

Command

build

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

No response

Description

Good day,
we need to build our app with --base-href as we include it in legacy code
ng build --base-href /prefix/
We are running it in SSR and multi language setup - SSR 19.2.5 and using subPath
"en-CA": { "translation": "en.json", "subPath": "" }, "fr-CA": { "translation": "fr.json", "subPath": "fr" },

angular-app-engine-manifest.mjs has basePath defined from build line above
export default { basePath: '/prefix/',
in this configuration only first language is loaded but not the second one. To get second language working correctly we need to edit basePath in angular-app-engine-manifest.mjs to "/" after that everything works correctly.

Is there other way around, can we configure basePath for angular-app-engine-manifest?

We used to have a wrapper in angular18 which was loading each language build.

Minimal Reproduction

ng build --base-href /prefix/

Exception or Error


Your Environment

Angular CLI: 19.2.6
Node: 20.19.0
Package Manager: npm 
OS: linux x64

Angular: 19.2.5
... animations, common, compiler, compiler-cli, core, forms
... localize, platform-browser, platform-browser-dynamic
... platform-server, router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1902.6
@angular-devkit/build-angular   19.2.6
@angular-devkit/core            19.2.6
@angular-devkit/schematics      19.2.6
@angular/cli                    19.2.6
@angular/ssr                    19.2.6
@schematics/angular             19.2.6
rxjs                            7.8.2
typescript                      5.5.4
zone.js                         0.15.0

Anything else relevant?

No response

@alan-agius4
Copy link
Collaborator

Hi, I'm having trouble understanding the issue. Since the base-href is set to /prefix/, the English version is accessible at http://example.com/prefix, and the French version is available at http://example.com/prefix/fr, are you seeing something different?

@alan-agius4 alan-agius4 added needs: more info Reporter must clarify the issue area: @angular/ssr labels Apr 9, 2025
@regiss
Copy link
Author

regiss commented Apr 9, 2025

Hi thank you for looking at it.
As we need to include angular app in legacy code we build it with /prefix/ but this prefix is then removed with nginx via rewrite rule.
Doing is this way we can run angular app (we define urls which go inside the angular app) along the legacy code. So basically prefix is needed just to distinguish between legacy and angular app.
Hope it makes sense.

@alan-agius4
Copy link
Collaborator

From what I understand, the prefix is stripped from the incoming request. So when a user accesses http://example.com/prefix/fr, Nginx internally rewrites it to http://example.com/fr.

For example:

location /prefix/ {
    rewrite ^/prefix/(.*)$ /$1 break;
}

If this understanding is correct, could you please elaborate on why the internal URL rewrite is necessary?
And if this is not accurate, I’d appreciate it if you could clarify your setup or alternatively, provide a minimal, runnable reproduction.

@regiss
Copy link
Author

regiss commented Apr 10, 2025

If this understanding is correct, could you please elaborate on why the internal URL rewrite is necessary?
yes this is correct

The reason is running angular app along with legacy code in java/php.
For instance:

legacy code in java (here we need to load legacy css and javascript files) - urls

  • /url01
  • /url02

angular app (here we need to load angular related js files - that's why we use prefix) urls:

  • /url03
  • /url04

prefix is stripped via nginx so urls can have same format as in legacy app.
Thank you for your insights.

@alan-agius4
Copy link
Collaborator

This statement prefix is stripped via nginx so urls can have same format as in legacy app. contradicts the confirmation that http://example.com/prefix/url03 is being rewritten to http://example.com/url03. Instead, it seems that the reverse is happening: an incoming request to http://example.com/url03 is being internally rewritten to http://example.com/prefix/url03 in order to match the legacy application’s URL format. IE: http://example.com/url03 -> http://example.com/prefix/url03 -> http://example.com/url03. But than again this should work with basePath: '/prefix/ given that the URL has been re-written internally.

  1. Could you please clarify the request flow in more detail? Specifically, how does it work from:
    Incoming Request URL -> NGINX -> Server -> Outgoing Response URL?

  2. Since the Angular Router relies on the base-href, how is this being handled by your application during client side rendering navigation?

  3. Can you share the rewrite rule of Nginx?

@regiss
Copy link
Author

regiss commented Apr 10, 2025

we have 2 nginx server

  1. is handling incoming requests
  2. nginx:8002 is handling angular app

incoming request: http://url_03
nginx01 (proxy pass to second nginx):

location ~ ^/(url_03|url_04) { proxy_pass http://nginx:8002}  
location /prefix { proxy_pass http://nginx:8002}

nginx02:

location / {                                  
  index index.html;                 
  try_files $uri @universal;                  
}                                             
                                              
location ~ ^/(second_language|)$ {                         
  try_files $uri @universal;                  
}                                             
                                              
location /prefix {                       
   rewrite /prefix/(.*) /$1;             
}                                             
                                              
#Transfer to angular universal handler        
location @universal{                          
	proxy_pass http://angular:4000;           
}                                             

angular is started as node /path_to_dist/server/server.mjs

when I alter prefix in server/angular-app-engine-manifest.mjs from /prefix to / both languages works
for instance:

  • /url_03 in source code base href="/https/github.com/prefix/" (adjusted)
  • /fr/url_03 in source code base href="/https/github.com/prefix/fr/" (adjusted)

Thank you very much for help with it

@alan-agius4
Copy link
Collaborator

During client-side navigation, if you set the base-href to /prefix (In this case I am assuming that /cms-scripts is your /prefix), the client-side routing will generate URLs like http://example.com/cms-scripts/fr/url_03 when navigating within the application (without hitting the server).

Instead of removing the base-href from the request, you can adjust the Nginx configuration to handle it instead if you'd want users to be able to access http://example.com/cms-scripts/fr/url_03.

For example:

-location / {
-  index index.html;
-  try_files $uri @universal;
-}

-location ~ ^/(second_language|)$ {
-  try_files $uri @universal;
-}

-location /prefix {
-  rewrite /prefix/(.*) /$1;
-}

+ location / {
+  if ($request_uri !~ ^/prefix) {
+    rewrite ^/(.*)$ /prefix/$1 break;
+  }
   proxy_pass http://angular:4000;
}

Alternatively, if you prefer to hide the /prefix from users during navigation (i.e., they should always access URLs like http://example.com/url03 rather than http://example.com/prefix/url03), you can set the baseHref to / (the default) and specify the deployUrl as /prefix/. This way, the resources will be loaded via /prefix (e.g., /prefix/main.js), but navigation will still occur as http://example.com/url03.

The Nginx configuration in this case would look like:

location / {
   if ($request_uri ~ ^/prefix) {
      rewrite ^/prefix(.*)$ $1 break;
   }

   proxy_pass http://angular:4000;
}

Note: Both approaches can also be implemented in a single Nginx instance, removing the need for two separate Nginx configurations.

@regiss
Copy link
Author

regiss commented Apr 10, 2025

Thank you very much for your time and advises.
I give a go and let you know.

@regiss
Copy link
Author

regiss commented Apr 11, 2025

We prefer hiding /prefix
changing it to baseHref to / and specify the deployUrl as /prefix/ did the trick. However then both languages have same deployUrl for js,css in index.csr.html

Default one should be: script src="/https/github.com/prefix/main
Second language (fr) should be: script src="/https/github.com/prefix/fr/main but is same as above
I have achieved that with another nginx rule (using http_referer for it)

if ($http_referer ~* "/fr") {
	# Do something if Referer contains '/fr'
	rewrite /prefix/(.*) /prefix/fr/$1 break;
}

I assume there is no way to define deploy url per language, right?
We used to have deployUrl, but we replaced it with baseHref as I though (deployUrl is deprecated and should be replaced by baseHref) I guess it's safe to use it onwards

@alan-agius4
Copy link
Collaborator

alan-agius4 commented Apr 11, 2025

No, it's not possible to define a separate deploy URL for each language. However, using deployUrl is supported.

@regiss
Copy link
Author

regiss commented Apr 11, 2025

Would you say that rewrite base on $http_referer is way to go?

@alan-agius4
Copy link
Collaborator

Using $http_referer is also a good approach, you could also set the deploy url to relative Ie: deployUrl: "prefix/" which will case the "prefix" to follow the locale in the url and re-write that.

Example:
http://example.com/fr/prefix/main.js
http://example.com/prefix/main.js

if ($request_uri ~* /prefix/) {
    rewrite /(.*)/prefix/(.*) /$1$2 break;
}

Note: this approach will break url() references used for critical CSS inlining so you might need to disable that.

@alan-agius4 alan-agius4 closed this as not planned Won't fix, can't repro, duplicate, stale Apr 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: @angular/ssr needs: more info Reporter must clarify the issue
Projects
None yet
Development

No branches or pull requests

2 participants