@@ -235,6 +235,40 @@ pub async fn process_deploy_nextjs_ssr(env: Environment, config: Config) -> Resu
235235 . map ( |path| format ! ( "{}/" , path) )
236236 . unwrap_or_default ( ) ;
237237
238+ // Next.js copies the project's .env* files into .next/standalone/, so an
239+ // unfiltered upload would ship the developer's local (often development)
240+ // env straight into the production runtime directory — and `--delete`
241+ // would remove any operator-managed .env on the server. Env files are
242+ // excluded on both sides: local ones never upload, server ones survive
243+ // (rsync does not delete excluded destination files without
244+ // --delete-excluded). Runtime env is server-managed — ecosystem config or
245+ // a server-side .env. Anchored to the app roots so bundled node_modules
246+ // content is not affected.
247+ let env_file_excludes: Vec < String > = {
248+ let mut patterns = vec ! [ "/.env*" . to_string( ) ] ;
249+ if !runtime_prefix. is_empty ( ) {
250+ patterns. push ( format ! ( "/{}.env*" , runtime_prefix) ) ;
251+ }
252+ patterns
253+ } ;
254+
255+ let local_env_file = [
256+ format ! ( "{}/.env" , standalone_dir) ,
257+ format ! ( "{}/{}.env" , standalone_dir, runtime_prefix) ,
258+ ]
259+ . iter ( )
260+ . any ( |path| std:: path:: Path :: new ( path) . exists ( ) ) ;
261+ if local_env_file {
262+ println ! (
263+ "{} {}" ,
264+ succeed_symbol( ) ,
265+ succeed_message(
266+ "Local .env* files found in the standalone build — not uploaded. \
267+ Runtime env comes from the server (ecosystem config or server-side .env).",
268+ )
269+ ) ;
270+ }
271+
238272 // (local_source, remote_destination)
239273 // .next/standalone contents go to the root of remote_path.
240274 // .next/static and public go into the runtime directory that contains
@@ -299,6 +333,9 @@ pub async fn process_deploy_nextjs_ssr(env: Environment, config: Config) -> Resu
299333 "--exclude" . to_string ( ) ,
300334 "logs/" . to_string ( ) ,
301335 ] ) ;
336+ for pattern in & env_file_excludes {
337+ rsync_args. extend ( [ "--exclude" . to_string ( ) , pattern. clone ( ) ] ) ;
338+ }
302339 }
303340
304341 rsync_args. extend ( [
0 commit comments