diff --git a/server/szurubooru/api/post_api.py b/server/szurubooru/api/post_api.py
index 6200d70..ed049f3 100644
--- a/server/szurubooru/api/post_api.py
+++ b/server/szurubooru/api/post_api.py
@@ -9,6 +9,8 @@ class PostListApi(BaseApi):
         tag_names = ctx.get_param_as_list('tags', required=True)
         safety = ctx.get_param_as_string('safety', required=True)
         source = ctx.get_param_as_string('source', required=False, default=None)
+        if ctx.has_param('contentUrl') and not source:
+            source = ctx.get_param_as_string('contentUrl')
         relations = ctx.get_param_as_list('relations', required=False) or []
         notes = ctx.get_param_as_list('notes', required=False) or []
         flags = ctx.get_param_as_list('flags', required=False) or []
@@ -47,6 +49,8 @@ class PostDetailApi(BaseApi):
         if ctx.has_param('source'):
             auth.verify_privilege(ctx.user, 'posts:edit:source')
             posts.update_post_source(post, ctx.get_param_as_string('source'))
+        elif ctx.has_param('contentUrl'):
+            posts.update_post_source(post, ctx.get_param_as_string('contentUrl'))
         if ctx.has_param('relations'):
             auth.verify_privilege(ctx.user, 'posts:edit:relations')
             posts.update_post_relations(post, ctx.get_param_as_list('relations'))
diff --git a/server/szurubooru/tests/api/test_post_creating.py b/server/szurubooru/tests/api/test_post_creating.py
index 54e9a49..c60043c 100644
--- a/server/szurubooru/tests/api/test_post_creating.py
+++ b/server/szurubooru/tests/api/test_post_creating.py
@@ -3,7 +3,7 @@ import os
 import unittest.mock
 import pytest
 from szurubooru import api, db, errors
-from szurubooru.func import posts, tags, snapshots
+from szurubooru.func import posts, tags, snapshots, net
 
 @pytest.fixture(autouse=True)
 def inject_config(config_injector):
@@ -105,6 +105,71 @@ def test_creating_full_posts(context_factory, post_factory, user_factory):
         tags.export_to_json.assert_called_once_with()
         snapshots.save_entity_creation.assert_called_once_with(post, auth_user)
 
+def test_creating_from_url_saves_source(
+        config_injector, context_factory, post_factory, user_factory):
+    auth_user = user_factory(rank='regular_user')
+    post = post_factory()
+    db.session.add(post)
+    db.session.flush()
+
+    with unittest.mock.patch('szurubooru.func.net.download'), \
+        unittest.mock.patch('szurubooru.func.tags.export_to_json'), \
+        unittest.mock.patch('szurubooru.func.snapshots.save_entity_creation'), \
+        unittest.mock.patch('szurubooru.func.posts.serialize_post_with_details'), \
+        unittest.mock.patch('szurubooru.func.posts.create_post'), \
+        unittest.mock.patch('szurubooru.func.posts.update_post_source'):
+        config_injector({
+            'ranks': ['anonymous', 'regular_user'],
+            'privileges': {'posts:create': 'regular_user'},
+        })
+        net.download.return_value = b'content'
+        posts.create_post.return_value = post
+        api.PostListApi().post(
+            context_factory(
+                input={
+                    'safety': 'safe',
+                    'tags': ['tag1', 'tag2'],
+                    'contentUrl': 'example.com',
+                },
+                user=auth_user))
+        net.download.assert_called_once_with('example.com')
+        posts.create_post.assert_called_once_with(
+            b'content', ['tag1', 'tag2'], auth_user)
+        posts.update_post_source.assert_called_once_with(post, 'example.com')
+
+def test_creating_from_url_with_source_specified(
+        config_injector, context_factory, post_factory, user_factory):
+    auth_user = user_factory(rank='regular_user')
+    post = post_factory()
+    db.session.add(post)
+    db.session.flush()
+
+    with unittest.mock.patch('szurubooru.func.net.download'), \
+        unittest.mock.patch('szurubooru.func.tags.export_to_json'), \
+        unittest.mock.patch('szurubooru.func.snapshots.save_entity_creation'), \
+        unittest.mock.patch('szurubooru.func.posts.serialize_post_with_details'), \
+        unittest.mock.patch('szurubooru.func.posts.create_post'), \
+        unittest.mock.patch('szurubooru.func.posts.update_post_source'):
+        config_injector({
+            'ranks': ['anonymous', 'regular_user'],
+            'privileges': {'posts:create': 'regular_user'},
+        })
+        net.download.return_value = b'content'
+        posts.create_post.return_value = post
+        api.PostListApi().post(
+            context_factory(
+                input={
+                    'safety': 'safe',
+                    'tags': ['tag1', 'tag2'],
+                    'contentUrl': 'example.com',
+                    'source': 'example2.com',
+                },
+                user=auth_user))
+        net.download.assert_called_once_with('example.com')
+        posts.create_post.assert_called_once_with(
+            b'content', ['tag1', 'tag2'], auth_user)
+        posts.update_post_source.assert_called_once_with(post, 'example2.com')
+
 @pytest.mark.parametrize('field', ['tags', 'safety'])
 def test_trying_to_omit_mandatory_field(context_factory, user_factory, field):
     input = {
diff --git a/server/szurubooru/tests/api/test_post_updating.py b/server/szurubooru/tests/api/test_post_updating.py
index 7498f53..b241604 100644
--- a/server/szurubooru/tests/api/test_post_updating.py
+++ b/server/szurubooru/tests/api/test_post_updating.py
@@ -3,7 +3,7 @@ import os
 import unittest.mock
 import pytest
 from szurubooru import api, db, errors
-from szurubooru.func import posts, tags, snapshots
+from szurubooru.func import posts, tags, snapshots, net
 
 def test_post_updating(
         config_injector, context_factory, post_factory, user_factory, fake_datetime):
@@ -73,6 +73,59 @@ def test_post_updating(
         snapshots.save_entity_modification.assert_called_once_with(post, auth_user)
         assert post.last_edit_time == datetime.datetime(1997, 1, 1)
 
+def test_uploading_from_url_saves_source(
+        config_injector, context_factory, post_factory, user_factory):
+    config_injector({
+        'ranks': ['anonymous', 'regular_user'],
+        'privileges': {'posts:edit:content': 'regular_user'},
+    })
+    post = post_factory()
+    db.session.add(post)
+    db.session.flush()
+    with unittest.mock.patch('szurubooru.func.net.download'), \
+        unittest.mock.patch('szurubooru.func.tags.export_to_json'), \
+        unittest.mock.patch('szurubooru.func.snapshots.save_entity_modification'), \
+        unittest.mock.patch('szurubooru.func.posts.serialize_post_with_details'), \
+        unittest.mock.patch('szurubooru.func.posts.update_post_content'), \
+        unittest.mock.patch('szurubooru.func.posts.update_post_source'):
+        net.download.return_value = b'content'
+        api.PostDetailApi().put(
+            context_factory(
+                input={'contentUrl': 'example.com'},
+                user=user_factory(rank='regular_user')),
+            post.post_id)
+        net.download.assert_called_once_with('example.com')
+        posts.update_post_content.assert_called_once_with(post, b'content')
+        posts.update_post_source.assert_called_once_with(post, 'example.com')
+
+def test_uploading_from_url_with_source_specified(
+        config_injector, context_factory, post_factory, user_factory):
+    config_injector({
+        'ranks': ['anonymous', 'regular_user'],
+        'privileges': {
+            'posts:edit:content': 'regular_user',
+            'posts:edit:source': 'regular_user',
+        },
+    })
+    post = post_factory()
+    db.session.add(post)
+    db.session.flush()
+    with unittest.mock.patch('szurubooru.func.net.download'), \
+        unittest.mock.patch('szurubooru.func.tags.export_to_json'), \
+        unittest.mock.patch('szurubooru.func.snapshots.save_entity_modification'), \
+        unittest.mock.patch('szurubooru.func.posts.serialize_post_with_details'), \
+        unittest.mock.patch('szurubooru.func.posts.update_post_content'), \
+        unittest.mock.patch('szurubooru.func.posts.update_post_source'):
+        net.download.return_value = b'content'
+        api.PostDetailApi().put(
+            context_factory(
+                input={'contentUrl': 'example.com', 'source': 'example2.com'},
+                user=user_factory(rank='regular_user')),
+            post.post_id)
+        net.download.assert_called_once_with('example.com')
+        posts.update_post_content.assert_called_once_with(post, b'content')
+        posts.update_post_source.assert_called_once_with(post, 'example2.com')
+
 def test_trying_to_update_non_existing(context_factory, user_factory):
     with pytest.raises(posts.PostNotFoundError):
         api.PostDetailApi().put(