diff --git a/packages/angular_devkit/build_angular/src/browser/specs/output-hashing_spec.ts b/packages/angular_devkit/build_angular/src/browser/specs/output-hashing_spec.ts
deleted file mode 100644
index e13ed05fbabe..000000000000
--- a/packages/angular_devkit/build_angular/src/browser/specs/output-hashing_spec.ts
+++ /dev/null
@@ -1,195 +0,0 @@
-/**
- * @license
- * Copyright Google Inc. All Rights Reserved.
- *
- * Use of this source code is governed by an MIT-style license that can be
- * found in the LICENSE file at https://angular.io/license
- */
-
-import { Architect } from '@angular-devkit/architect';
-import { normalize } from '@angular-devkit/core';
-import {
-  browserBuild,
-  createArchitect,
-  host,
-  lazyModuleFiles,
-  lazyModuleFnImport,
-} from '../../test-utils';
-
-describe('Browser Builder output hashing', () => {
-  const target = { project: 'app', target: 'build' };
-  let architect: Architect;
-
-  beforeEach(async () => {
-    await host.initialize().toPromise();
-    architect = (await createArchitect(host.root())).architect;
-  });
-  afterEach(async () => host.restore().toPromise());
-
-  it('updates hash as content changes', async () => {
-    const OUTPUT_RE = /(main|styles|lazy\.module)\.([a-z0-9]+)\.(chunk|bundle)\.(js|css)$/;
-
-    function generateFileHashMap(): Map<string, string> {
-      const hashes = new Map<string, string>();
-
-      host
-        .scopedSync()
-        .list(normalize('./dist'))
-        .forEach(name => {
-          const matches = name.match(OUTPUT_RE);
-          if (matches) {
-            const [, module, hash] = matches;
-            hashes.set(module, hash);
-          }
-        });
-
-      return hashes;
-    }
-
-    function validateHashes(
-      oldHashes: Map<string, string>,
-      newHashes: Map<string, string>,
-      shouldChange: Array<string>,
-    ): void {
-      newHashes.forEach((hash, module) => {
-        if (hash == oldHashes.get(module)) {
-          if (shouldChange.includes(module)) {
-            throw new Error(
-              `Module "${module}" did not change hash (${hash}), but was expected to.`,
-            );
-          }
-        } else if (!shouldChange.includes(module)) {
-          throw new Error(`Module "${module}" changed hash (${hash}), but was not expected to.`);
-        }
-      });
-    }
-
-    let oldHashes: Map<string, string>;
-    let newHashes: Map<string, string>;
-
-    host.writeMultipleFiles(lazyModuleFiles);
-    host.writeMultipleFiles(lazyModuleFnImport);
-
-    const overrides = { outputHashing: 'all', extractCss: true };
-
-    // We must do several builds instead of a single one in watch mode, so that the output
-    // path is deleted on each run and only contains the most recent files.
-    await browserBuild(architect, host, target, overrides);
-
-    // Save the current hashes.
-    oldHashes = generateFileHashMap();
-    host.writeMultipleFiles(lazyModuleFiles);
-    host.writeMultipleFiles(lazyModuleFnImport);
-
-    await browserBuild(architect, host, target, overrides);
-    newHashes = generateFileHashMap();
-    validateHashes(oldHashes, newHashes, []);
-    oldHashes = newHashes;
-    host.writeMultipleFiles({ 'src/styles.css': 'body { background: blue; }' });
-
-    // Style hash should change.
-    await browserBuild(architect, host, target, overrides);
-    newHashes = generateFileHashMap();
-    validateHashes(oldHashes, newHashes, ['styles']);
-    oldHashes = newHashes;
-    host.writeMultipleFiles({ 'src/app/app.component.css': 'h1 { margin: 10px; }' });
-
-    // Main hash should change, since inline styles go in the main bundle.
-    await browserBuild(architect, host, target, overrides);
-    newHashes = generateFileHashMap();
-    validateHashes(oldHashes, newHashes, ['main']);
-    oldHashes = newHashes;
-    host.appendToFile('src/app/lazy/lazy.module.ts', `console.log(1);`);
-
-    // Lazy loaded bundle should change, and so should inline.
-    await browserBuild(architect, host, target, overrides);
-    newHashes = generateFileHashMap();
-    validateHashes(oldHashes, newHashes, ['lazy.module']);
-    oldHashes = newHashes;
-    host.appendToFile('src/main.ts', '');
-
-    // Nothing should have changed.
-    await browserBuild(architect, host, target, overrides);
-    newHashes = generateFileHashMap();
-    validateHashes(oldHashes, newHashes, []);
-  });
-
-  it('supports options', async () => {
-    host.writeMultipleFiles({ 'src/styles.css': `h1 { background: url('./spectrum.png')}` });
-    host.writeMultipleFiles(lazyModuleFiles);
-    host.writeMultipleFiles(lazyModuleFnImport);
-
-    // We must do several builds instead of a single one in watch mode, so that the output
-    // path is deleted on each run and only contains the most recent files.
-    // 'all' should hash everything.
-    await browserBuild(architect, host, target, { outputHashing: 'all', extractCss: true });
-
-    expect(host.fileMatchExists('dist', /runtime\.[0-9a-f]{20}\.js/)).toBeTruthy();
-    expect(host.fileMatchExists('dist', /main\.[0-9a-f]{20}\.js/)).toBeTruthy();
-    expect(host.fileMatchExists('dist', /polyfills\.[0-9a-f]{20}\.js/)).toBeTruthy();
-    expect(host.fileMatchExists('dist', /vendor\.[0-9a-f]{20}\.js/)).toBeTruthy();
-    expect(host.fileMatchExists('dist', /styles\.[0-9a-f]{20}\.css/)).toBeTruthy();
-    expect(host.fileMatchExists('dist', /spectrum\.[0-9a-f]{20}\.png/)).toBeTruthy();
-
-    // 'none' should hash nothing.
-    await browserBuild(architect, host, target, { outputHashing: 'none', extractCss: true });
-
-    expect(host.fileMatchExists('dist', /runtime\.[0-9a-f]{20}\.js/)).toBeFalsy();
-    expect(host.fileMatchExists('dist', /main\.[0-9a-f]{20}\.js/)).toBeFalsy();
-    expect(host.fileMatchExists('dist', /polyfills\.[0-9a-f]{20}\.js/)).toBeFalsy();
-    expect(host.fileMatchExists('dist', /vendor\.[0-9a-f]{20}\.js/)).toBeFalsy();
-    expect(host.fileMatchExists('dist', /styles\.[0-9a-f]{20}\.css/)).toBeFalsy();
-    expect(host.fileMatchExists('dist', /spectrum\.[0-9a-f]{20}\.png/)).toBeFalsy();
-
-    // 'media' should hash css resources only.
-    await browserBuild(architect, host, target, { outputHashing: 'media', extractCss: true });
-
-    expect(host.fileMatchExists('dist', /runtime\.[0-9a-f]{20}\.js/)).toBeFalsy();
-    expect(host.fileMatchExists('dist', /main\.[0-9a-f]{20}\.js/)).toBeFalsy();
-    expect(host.fileMatchExists('dist', /polyfills\.[0-9a-f]{20}\.js/)).toBeFalsy();
-    expect(host.fileMatchExists('dist', /vendor\.[0-9a-f]{20}\.js/)).toBeFalsy();
-    expect(host.fileMatchExists('dist', /styles\.[0-9a-f]{20}\.css/)).toBeFalsy();
-    expect(host.fileMatchExists('dist', /spectrum\.[0-9a-f]{20}\.png/)).toBeTruthy();
-
-    // 'bundles' should hash bundles only.
-    await browserBuild(architect, host, target, { outputHashing: 'bundles', extractCss: true });
-    expect(host.fileMatchExists('dist', /runtime\.[0-9a-f]{20}\.js/)).toBeTruthy();
-    expect(host.fileMatchExists('dist', /main\.[0-9a-f]{20}\.js/)).toBeTruthy();
-    expect(host.fileMatchExists('dist', /polyfills\.[0-9a-f]{20}\.js/)).toBeTruthy();
-    expect(host.fileMatchExists('dist', /vendor\.[0-9a-f]{20}\.js/)).toBeTruthy();
-    expect(host.fileMatchExists('dist', /styles\.[0-9a-f]{20}\.css/)).toBeTruthy();
-    expect(host.fileMatchExists('dist', /spectrum\.[0-9a-f]{20}\.png/)).toBeFalsy();
-  });
-
-  it('does not hash non injected styles', async () => {
-    const overrides = {
-      outputHashing: 'all',
-      extractCss: true,
-      styles: [{ input: 'src/styles.css', inject: false }],
-    };
-
-    await browserBuild(architect, host, target, overrides);
-
-    expect(host.fileMatchExists('dist', /styles\.[0-9a-f]{20}\.js/)).toBeFalsy();
-    expect(host.fileMatchExists('dist', /styles\.[0-9a-f]{20}\.js.map/)).toBeFalsy();
-    expect(host.scopedSync().exists(normalize('dist/styles.css'))).toBe(true);
-    expect(host.scopedSync().exists(normalize('dist/styles.css.map'))).toBe(true);
-  });
-
-  it('does not hash non injected styles when optimization is enabled', async () => {
-    host.writeMultipleFiles({ 'src/styles.css': 'body { background: blue; }' });
-
-    const overrides = {
-      outputHashing: 'all',
-      extractCss: true,
-      optimization: true,
-      styles: [{ input: 'src/styles.css', inject: false }],
-    };
-
-    await browserBuild(architect, host, target, overrides);
-    expect(host.fileMatchExists('dist', /styles\.[0-9a-f]{20}\.js/)).toBeFalsy();
-    expect(host.fileMatchExists('dist', /styles\.[0-9a-f]{20}\.js.map/)).toBeFalsy();
-    expect(host.scopedSync().exists(normalize('dist/styles.css'))).toBe(true);
-    expect(host.scopedSync().exists(normalize('dist/styles.css.map'))).toBe(true);
-  });
-});
diff --git a/packages/angular_devkit/build_angular/src/browser/tests/options/output-hashing_spec.ts b/packages/angular_devkit/build_angular/src/browser/tests/options/output-hashing_spec.ts
new file mode 100644
index 000000000000..6f046fcede67
--- /dev/null
+++ b/packages/angular_devkit/build_angular/src/browser/tests/options/output-hashing_spec.ts
@@ -0,0 +1,176 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+
+import { buildWebpackBrowser } from '../../index';
+import { OutputHashing } from '../../schema';
+import { BASE_OPTIONS, BROWSER_BUILDER_INFO, describeBuilder } from '../setup';
+
+describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => {
+  describe('Option: "outputHashing"', () => {
+    beforeEach(async () => {
+      // Application code is not needed for asset tests
+      await harness.writeFile('src/main.ts', '');
+    });
+
+    it('hashes all filenames when set to "all"', async () => {
+      await harness.writeFile(
+        'src/styles.css',
+        `h1 { background: url('./spectrum.png')}`,
+      );
+
+      harness.useTarget('build', {
+        ...BASE_OPTIONS,
+        styles: ['src/styles.css'],
+        outputHashing: OutputHashing.All,
+      });
+
+      const { result } = await harness.executeOnce();
+      expect(result?.success).toBe(true);
+
+      expect(harness.hasFileMatch('dist', /runtime\.[0-9a-f]{20}\.js$/)).toBeTrue();
+      expect(harness.hasFileMatch('dist', /main\.[0-9a-f]{20}\.js$/)).toBeTrue();
+      expect(harness.hasFileMatch('dist', /polyfills\.[0-9a-f]{20}\.js$/)).toBeTrue();
+      expect(harness.hasFileMatch('dist', /styles\.[0-9a-f]{20}\.css$/)).toBeTrue();
+      expect(harness.hasFileMatch('dist', /spectrum\.[0-9a-f]{20}\.png$/)).toBeTrue();
+    });
+
+    it(`doesn't hash any filenames when not set`, async () => {
+      await harness.writeFile(
+        'src/styles.css',
+        `h1 { background: url('./spectrum.png')}`,
+      );
+
+      harness.useTarget('build', {
+        ...BASE_OPTIONS,
+        styles: ['src/styles.css'],
+      });
+
+      const { result } = await harness.executeOnce();
+      expect(result?.success).toBe(true);
+
+      expect(harness.hasFileMatch('dist', /runtime\.[0-9a-f]{20}\.js$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /main\.[0-9a-f]{20}\.js$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /polyfills\.[0-9a-f]{20}\.js$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /styles\.[0-9a-f]{20}\.css$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /spectrum\.[0-9a-f]{20}\.png$/)).toBeFalse();
+    });
+
+    it(`doesn't hash any filenames when set to "none"`, async () => {
+      await harness.writeFile(
+        'src/styles.css',
+        `h1 { background: url('./spectrum.png')}`,
+      );
+
+      harness.useTarget('build', {
+        ...BASE_OPTIONS,
+        styles: ['src/styles.css'],
+        outputHashing: OutputHashing.None,
+      });
+
+      const { result } = await harness.executeOnce();
+      expect(result?.success).toBe(true);
+
+      expect(harness.hasFileMatch('dist', /runtime\.[0-9a-f]{20}\.js$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /main\.[0-9a-f]{20}\.js$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /polyfills\.[0-9a-f]{20}\.js$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /styles\.[0-9a-f]{20}\.css$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /spectrum\.[0-9a-f]{20}\.png$/)).toBeFalse();
+    });
+
+    it(`hashes CSS resources filenames only when set to "media"`, async () => {
+      await harness.writeFile(
+        'src/styles.css',
+        `h1 { background: url('./spectrum.png')}`,
+      );
+
+      harness.useTarget('build', {
+        ...BASE_OPTIONS,
+        styles: ['src/styles.css'],
+        outputHashing: OutputHashing.Media,
+      });
+
+      const { result } = await harness.executeOnce();
+      expect(result?.success).toBe(true);
+
+      expect(harness.hasFileMatch('dist', /runtime\.[0-9a-f]{20}\.js$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /main\.[0-9a-f]{20}\.js$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /polyfills\.[0-9a-f]{20}\.js$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /styles\.[0-9a-f]{20}\.css$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /spectrum\.[0-9a-f]{20}\.png$/)).toBeTrue();
+    });
+
+    it(`hashes bundles filenames only when set to "bundles"`, async () => {
+      await harness.writeFile(
+        'src/styles.css',
+        `h1 { background: url('./spectrum.png')}`,
+      );
+
+      harness.useTarget('build', {
+        ...BASE_OPTIONS,
+        styles: ['src/styles.css'],
+        outputHashing: OutputHashing.Bundles,
+      });
+
+      const { result } = await harness.executeOnce();
+      expect(result?.success).toBe(true);
+
+      expect(harness.hasFileMatch('dist', /runtime\.[0-9a-f]{20}\.js$/)).toBeTrue();
+      expect(harness.hasFileMatch('dist', /main\.[0-9a-f]{20}\.js$/)).toBeTrue();
+      expect(harness.hasFileMatch('dist', /polyfills\.[0-9a-f]{20}\.js$/)).toBeTrue();
+      expect(harness.hasFileMatch('dist', /styles\.[0-9a-f]{20}\.css$/)).toBeTrue();
+      expect(harness.hasFileMatch('dist', /spectrum\.[0-9a-f]{20}\.png$/)).toBeFalse();
+    });
+
+    it('does not hash non injected styles', async () => {
+      harness.useTarget('build', {
+        ...BASE_OPTIONS,
+        outputHashing: OutputHashing.All,
+        styles: [{
+          input: 'src/styles.css',
+          inject: false,
+        }],
+      });
+
+      const { result } = await harness.executeOnce();
+      expect(result?.success).toBe(true);
+
+      expect(harness.hasFileMatch('dist', /styles\.[0-9a-f]{20}\.js$/)).toBeFalse();
+      expect(harness.hasFileMatch('dist', /styles\.[0-9a-f]{20}\.js.map$/)).toBeFalse();
+      harness.expectFile('dist/styles.css').toExist();
+      harness.expectFile('dist/styles.css.map').toExist();
+    });
+
+    it('does not override different files which has the same filenames when hashing is "none"', async () => {
+      await harness.writeFiles({
+        'src/styles.css': `
+        h1 { background: url('./test.svg')}
+        h2 { background: url('./small/test.svg')}
+      `,
+        './src/test.svg': `<svg xmlns="http://www.w3.org/2000/svg">
+        <text x="20" y="20" font-size="20" fill="red">Hello World</text>
+      </svg>`,
+        './src/small/test.svg': `<svg xmlns="http://www.w3.org/2000/svg">
+        <text x="10" y="10" font-size="10" fill="red">Hello World</text>
+      </svg>`,
+      });
+
+      harness.useTarget('build', {
+        ...BASE_OPTIONS,
+        styles: ['src/styles.css'],
+        outputHashing: OutputHashing.None,
+      });
+
+      const { result } = await harness.executeOnce();
+      expect(result?.success).toBe(true);
+
+      harness.expectFile('dist/test.svg').toExist();
+      harness.expectFile('dist/small-test.svg').toExist();
+    });
+  });
+});
diff --git a/packages/angular_devkit/build_angular/src/testing/builder-harness.ts b/packages/angular_devkit/build_angular/src/testing/builder-harness.ts
index ec841b36807a..577ba868adc2 100644
--- a/packages/angular_devkit/build_angular/src/testing/builder-harness.ts
+++ b/packages/angular_devkit/build_angular/src/testing/builder-harness.ts
@@ -312,6 +312,12 @@ export class BuilderHarness<T> {
     return this.host.scopedSync().exists(normalize(path));
   }
 
+  hasFileMatch(directory: string, pattern: RegExp): boolean {
+    return this.host.scopedSync()
+      .list(normalize(directory))
+      .some(name => pattern.test(name));
+  }
+
   readFile(path: string): string {
     const content = this.host.scopedSync().read(normalize(path));
 
diff --git a/packages/angular_devkit/build_angular/src/webpack/configs/common.ts b/packages/angular_devkit/build_angular/src/webpack/configs/common.ts
index 781e4e64d70b..46b4900f7533 100644
--- a/packages/angular_devkit/build_angular/src/webpack/configs/common.ts
+++ b/packages/angular_devkit/build_angular/src/webpack/configs/common.ts
@@ -249,7 +249,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
     return prev;
   }, []);
 
-    // Add a new asset for each entry.
+  // Add a new asset for each entry.
   for (const script of globalScriptsByBundleName) {
     // Lazy scripts don't get a hash, otherwise they can't be loaded by name.
     const hash = script.inject ? hashFormat.script : '';
diff --git a/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts b/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts
index 822c73de4102..4d4192a15b42 100644
--- a/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts
+++ b/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts
@@ -17,7 +17,7 @@ import {
   RemoveHashPlugin,
   SuppressExtractedTextChunksWebpackPlugin,
 } from '../plugins';
-import { getOutputHashFormat, normalizeExtraEntryPoints } from '../utils/helpers';
+import { assetNameTemplateFactory, getOutputHashFormat, normalizeExtraEntryPoints } from '../utils/helpers';
 
 // tslint:disable-next-line:no-big-function
 export function getStylesConfig(wco: WebpackConfigOptions) {
@@ -154,6 +154,8 @@ export function getStylesConfig(wco: WebpackConfigOptions) {
     },
   ];
 
+  const assetNameTemplate = assetNameTemplateFactory(hashFormat);
+
   const postcssOptionsCreator = (sourceMap: boolean, extracted: boolean | undefined) => {
     return (loader: webpack.loader.LoaderContext) => ({
       map: sourceMap && {
@@ -183,7 +185,7 @@ export function getStylesConfig(wco: WebpackConfigOptions) {
           deployUrl: buildOptions.deployUrl,
           resourcesOutputPath: buildOptions.resourcesOutputPath,
           loader,
-          filename: `[name]${hashFormat.file}.[ext]`,
+          filename: assetNameTemplate,
           emitFile: buildOptions.platform !== 'server',
           extracted,
         }),
diff --git a/packages/angular_devkit/build_angular/src/webpack/plugins/postcss-cli-resources.ts b/packages/angular_devkit/build_angular/src/webpack/plugins/postcss-cli-resources.ts
index ff8242b9ceb3..dd44f6057388 100644
--- a/packages/angular_devkit/build_angular/src/webpack/plugins/postcss-cli-resources.ts
+++ b/packages/angular_devkit/build_angular/src/webpack/plugins/postcss-cli-resources.ts
@@ -31,7 +31,7 @@ export interface PostcssCliResourcesOptions {
   rebaseRootRelative?: boolean;
   /** CSS is extracted to a `.css` or is embedded in a `.js` file. */
   extracted?: boolean;
-  filename: string;
+  filename: (resourcePath: string) => string;
   loader: webpack.loader.LoaderContext;
   emitFile: boolean;
 }
@@ -113,10 +113,11 @@ export default function(options?: PostcssCliResourcesOptions): Plugin {
         }
 
         let outputPath = interpolateName(
-          { resourcePath: result } as webpack.loader.LoaderContext,
-          filename,
-          { content },
-        );
+          {  resourcePath: result } as webpack.loader.LoaderContext,
+          filename(result),
+          { content, context: loader.context || loader.rootContext },
+        )
+        .replace(/\\|\//g, '-');
 
         if (resourcesOutputPath) {
           outputPath = path.posix.join(resourcesOutputPath, outputPath);
diff --git a/packages/angular_devkit/build_angular/src/webpack/utils/helpers.ts b/packages/angular_devkit/build_angular/src/webpack/utils/helpers.ts
index 92c5e002fd75..a64dbe8048d1 100644
--- a/packages/angular_devkit/build_angular/src/webpack/utils/helpers.ts
+++ b/packages/angular_devkit/build_angular/src/webpack/utils/helpers.ts
@@ -7,6 +7,7 @@
  */
 
 import { basename, normalize } from '@angular-devkit/core';
+import * as path from 'path';
 import { ScriptTarget } from 'typescript';
 import { Options, SourceMapDevToolPlugin } from 'webpack';
 import { ExtraEntryPoint, ExtraEntryPointClass } from '../../browser/schema';
@@ -124,3 +125,30 @@ export function getWatchOptions(poll: number | undefined): Options.WatchOptions
     ignored: poll === undefined ? undefined : withWebpackFourOrFive(/[\\\/]node_modules[\\\/]/, 'node_modules/**'),
   };
 }
+
+export function assetNameTemplateFactory(hashFormat: HashFormat): (resourcePath: string) => string {
+  const visitedFiles = new Map<string, string>();
+
+  return (resourcePath: string) => {
+    if (hashFormat.file) {
+      // File names are hashed therefore we don't need to handle files with the same file name.
+      return `[name]${hashFormat.file}.[ext]`;
+    }
+
+    const filename = path.basename(resourcePath);
+    // Check if the file with the same name has already been processed.
+    const visited = visitedFiles.get(filename);
+    if (!visited) {
+      // Not visited.
+      visitedFiles.set(filename, resourcePath);
+
+      return filename;
+    } else if (visited === resourcePath) {
+      // Same file.
+      return filename;
+    }
+
+    // File has the same name but it's in a different location.
+    return '[path][name].[ext]';
+  };
+}